Skip to content

Commit

Permalink
fix: crash on x86_64 systems that support memory protection keys (den…
Browse files Browse the repository at this point in the history
…oland#1318)

Co-authored-by: Matt Mastracci <[email protected]>
  • Loading branch information
piscisaureus and mmastrac committed Sep 12, 2023
1 parent 6eb4b81 commit 53e048f
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 5 deletions.
58 changes: 58 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <memory>

#include "support.h"
#include "unicode/locid.h"
Expand All @@ -17,9 +19,12 @@
#include "v8/include/v8.h"
#include "v8/src/api/api-inl.h"
#include "v8/src/api/api.h"
#include "v8/src/base/debug/stack_trace.h"
#include "v8/src/base/sys-info.h"
#include "v8/src/execution/isolate-utils-inl.h"
#include "v8/src/execution/isolate-utils.h"
#include "v8/src/flags/flags.h"
#include "v8/src/libplatform/default-platform.h"
#include "v8/src/objects/objects-inl.h"
#include "v8/src/objects/objects.h"
#include "v8/src/objects/smi.h"
Expand Down Expand Up @@ -2579,6 +2584,49 @@ v8::StartupData v8__SnapshotCreator__CreateBlob(
return self->CreateBlob(function_code_handling);
}

class UnprotectedDefaultPlatform : public v8::platform::DefaultPlatform {
using IdleTaskSupport = v8::platform::IdleTaskSupport;
using InProcessStackDumping = v8::platform::InProcessStackDumping;
using PriorityMode = v8::platform::PriorityMode;
using TracingController = v8::TracingController;

static constexpr int kMaxThreadPoolSize = 16;

public:
explicit UnprotectedDefaultPlatform(
int thread_pool_size, IdleTaskSupport idle_task_support,
std::unique_ptr<TracingController> tracing_controller = {},
PriorityMode priority_mode = PriorityMode::kDontApply)
: v8::platform::DefaultPlatform(thread_pool_size, idle_task_support,
std::move(tracing_controller),
priority_mode) {}

static std::unique_ptr<v8::Platform> New(
int thread_pool_size, IdleTaskSupport idle_task_support,
InProcessStackDumping in_process_stack_dumping,
std::unique_ptr<TracingController> tracing_controller = {},
PriorityMode priority_mode = PriorityMode::kDontApply) {
// This implementation is semantically equivalent to the implementation of
// `v8::platform::NewDefaultPlatform()`.
DCHECK_GE(thread_pool_size, 0);
if (thread_pool_size < 1) {
thread_pool_size =
std::max(v8::base::SysInfo::NumberOfProcessors() - 1, 1);
}
thread_pool_size = std::min(thread_pool_size, kMaxThreadPoolSize);
if (in_process_stack_dumping == InProcessStackDumping::kEnabled) {
v8::base::debug::EnableInProcessStackDumping();
}
return std::make_unique<UnprotectedDefaultPlatform>(
thread_pool_size, idle_task_support, std::move(tracing_controller),
priority_mode);
}

v8::ThreadIsolatedAllocator* GetThreadIsolatedAllocator() override {
return nullptr;
}
};

v8::Platform* v8__Platform__NewDefaultPlatform(int thread_pool_size,
bool idle_task_support) {
return v8::platform::NewDefaultPlatform(
Expand All @@ -2589,6 +2637,16 @@ v8::Platform* v8__Platform__NewDefaultPlatform(int thread_pool_size,
.release();
}

v8::Platform* v8__Platform__NewUnprotectedDefaultPlatform(
int thread_pool_size, bool idle_task_support) {
return UnprotectedDefaultPlatform::New(
thread_pool_size,
idle_task_support ? v8::platform::IdleTaskSupport::kEnabled
: v8::platform::IdleTaskSupport::kDisabled,
v8::platform::InProcessStackDumping::kDisabled, nullptr)
.release();
}

v8::Platform* v8__Platform__NewSingleThreadedDefaultPlatform(
bool idle_task_support) {
return v8::platform::NewSingleThreadedDefaultPlatform(
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub use module::*;
pub use object::*;
pub use platform::new_default_platform;
pub use platform::new_single_threaded_default_platform;
pub use platform::new_unprotected_default_platform;
pub use platform::Platform;
pub use primitives::*;
pub use private::*;
Expand Down
54 changes: 54 additions & 0 deletions src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ extern "C" {
thread_pool_size: int,
idle_task_support: bool,
) -> *mut Platform;
fn v8__Platform__NewUnprotectedDefaultPlatform(
thread_pool_size: int,
idle_task_support: bool,
) -> *mut Platform;
fn v8__Platform__NewSingleThreadedDefaultPlatform(
idle_task_support: bool,
) -> *mut Platform;
Expand Down Expand Up @@ -58,6 +62,16 @@ pub struct Platform(Opaque);
/// If |idle_task_support| is enabled then the platform will accept idle
/// tasks (IdleTasksEnabled will return true) and will rely on the embedder
/// calling v8::platform::RunIdleTasks to process the idle tasks.
///
/// The default platform for v8 may include restrictions and caveats on thread
/// creation and initialization. This platform should only be used in cases
/// where v8 can be reliably initialized on the application's main thread, or
/// the parent thread to all threads in the system that will use v8.
///
/// One example of a restriction is the use of Memory Protection Keys (pkeys) on
/// modern Linux systems using modern Intel/AMD processors. This particular
/// technology requires that all threads using v8 are created as descendent
/// threads of the thread that called `v8::Initialize`.
#[inline(always)]
pub fn new_default_platform(
thread_pool_size: u32,
Expand All @@ -66,6 +80,18 @@ pub fn new_default_platform(
Platform::new(thread_pool_size, idle_task_support)
}

/// Creates a platform that is identical to the default platform, but does not
/// enforce thread-isolated allocations. This may reduce security in some cases,
/// so this method should be used with caution in cases where the threading
/// guarantees of `new_default_platform` cannot be upheld (generally for tests).
#[inline(always)]
pub fn new_unprotected_default_platform(
thread_pool_size: u32,
idle_task_support: bool,
) -> UniqueRef<Platform> {
Platform::new_unprotected(thread_pool_size, idle_task_support)
}

/// The same as new_default_platform() but disables the worker thread pool.
/// It must be used with the --single-threaded V8 flag.
///
Expand All @@ -88,6 +114,16 @@ impl Platform {
/// If |idle_task_support| is enabled then the platform will accept idle
/// tasks (IdleTasksEnabled will return true) and will rely on the embedder
/// calling v8::platform::RunIdleTasks to process the idle tasks.
///
/// The default platform for v8 may include restrictions and caveats on thread
/// creation and initialization. This platform should only be used in cases
/// where v8 can be reliably initialized on the application's main thread, or
/// the parent thread to all threads in the system that will use v8.
///
/// One example of a restriction is the use of Memory Protection Keys (pkeys)
/// on modern Linux systems using modern Intel/AMD processors. This particular
/// technology requires that all threads using v8 are created as descendent
/// threads of the thread that called `v8::Initialize`.
#[inline(always)]
pub fn new(
thread_pool_size: u32,
Expand All @@ -101,6 +137,24 @@ impl Platform {
}
}

/// Creates a platform that is identical to the default platform, but does not
/// enforce thread-isolated allocations. This may reduce security in some
/// cases, so this method should be used with caution in cases where the
/// threading guarantees of `new_default_platform` cannot be upheld (generally
/// for tests).
#[inline(always)]
pub fn new_unprotected(
thread_pool_size: u32,
idle_task_support: bool,
) -> UniqueRef<Self> {
unsafe {
UniqueRef::from_raw(v8__Platform__NewUnprotectedDefaultPlatform(
thread_pool_size.min(16) as i32,
idle_task_support,
))
}
}

/// The same as new() but disables the worker thread pool.
/// It must be used with the --single-threaded V8 flag.
///
Expand Down
2 changes: 1 addition & 1 deletion tests/slots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn setup() {
START.call_once(|| {
v8::V8::set_flags_from_string("--expose_gc");
v8::V8::initialize_platform(
v8::new_default_platform(0, false).make_shared(),
v8::new_unprotected_default_platform(0, false).make_shared(),
);
v8::V8::initialize();
});
Expand Down
2 changes: 1 addition & 1 deletion tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ mod setup {
"--no_freeze_flags_after_init --expose_gc --harmony-import-assertions --harmony-shadow-realm --allow_natives_syntax --turbo_fast_api_calls",
);
v8::V8::initialize_platform(
v8::new_default_platform(0, false).make_shared(),
v8::new_unprotected_default_platform(0, false).make_shared(),
);
v8::V8::initialize();
});
Expand Down
4 changes: 3 additions & 1 deletion tests/test_api_entropy_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ fn set_entropy_source() {
true
});

v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared());
v8::V8::initialize_platform(
v8::new_unprotected_default_platform(0, false).make_shared(),
);
v8::V8::initialize();

// Assumes that every isolate creates a PRNG from scratch, which is currently true.
Expand Down
4 changes: 3 additions & 1 deletion tests/test_api_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
#[test]
fn set_flags_from_string() {
v8::V8::set_flags_from_string("--use_strict");
v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared());
v8::V8::initialize_platform(
v8::new_unprotected_default_platform(0, false).make_shared(),
);
v8::V8::initialize();
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
Expand Down
4 changes: 3 additions & 1 deletion tests/test_platform_atomics_pump_message_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ fn atomics_pump_message_loop() {
v8::V8::set_flags_from_string(
"--allow-natives-syntax --harmony-sharedarraybuffer",
);
v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared());
v8::V8::initialize_platform(
v8::new_unprotected_default_platform(0, false).make_shared(),
);
v8::V8::initialize();
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
Expand Down

0 comments on commit 53e048f

Please sign in to comment.