// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/renderer/sandbox_status_extension_android.h"
#include <utility>
#include "base/android/build_info.h"
#include "base/check.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/task/thread_pool.h"
#include "chrome/common/url_constants.h"
#include "content/public/renderer/chrome_object_extensions_utils.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/seccomp_sandbox_status_android.h"
#include "content/public/renderer/v8_value_converter.h"
#include "gin/arguments.h"
#include "gin/function_template.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "v8/include/v8.h"
SandboxStatusExtension::SandboxStatusExtension(content::RenderFrame* frame)
: content::RenderFrameObserver(frame) {
// Don't do anything else for subframes.
if (!frame->IsMainFrame())
return;
frame->GetAssociatedInterfaceRegistry()
->AddInterface<chrome::mojom::SandboxStatusExtension>(base::BindRepeating(
&SandboxStatusExtension::OnSandboxStatusExtensionRequest,
base::RetainedRef(this)));
}
SandboxStatusExtension::~SandboxStatusExtension() {}
// static
void SandboxStatusExtension::Create(content::RenderFrame* frame) {
auto* extension = new SandboxStatusExtension(frame);
extension->AddRef(); // Balanced in OnDestruct().
}
void SandboxStatusExtension::OnDestruct() {
// This object is ref-counted, since a callback could still be in-flight.
Release();
}
void SandboxStatusExtension::DidClearWindowObject() {
Install();
}
void SandboxStatusExtension::AddSandboxStatusExtension() {
should_install_ = true;
}
void SandboxStatusExtension::OnSandboxStatusExtensionRequest(
mojo::PendingAssociatedReceiver<chrome::mojom::SandboxStatusExtension>
receiver) {
receiver_.Bind(std::move(receiver));
}
void SandboxStatusExtension::Install() {
if (!should_install_)
return;
blink::WebLocalFrame* web_frame = render_frame()->GetWebFrame();
v8::Isolate* isolate = web_frame->GetAgentGroupScheduler()->Isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = web_frame->MainWorldScriptContext();
if (context.IsEmpty())
return;
v8::Context::Scope context_scope(context);
v8::Local<v8::Object> chrome =
content::GetOrCreateChromeObject(isolate, context);
v8::Local<v8::Function> function;
bool success =
gin::CreateFunctionTemplate(
isolate,
base::BindRepeating(&SandboxStatusExtension::GetSandboxStatus, this))
->GetFunction(context)
.ToLocal(&function);
if (success) {
success = chrome
->Set(context,
gin::StringToSymbol(isolate, "getAndroidSandboxStatus"),
function)
.IsJust();
}
DCHECK(success);
}
void SandboxStatusExtension::GetSandboxStatus(gin::Arguments* args) {
if (!render_frame())
return;
if (render_frame()->GetWebFrame()->GetSecurityOrigin().Host() !=
chrome::kChromeUISandboxHost) {
args->ThrowTypeError("Not allowed on this origin");
return;
}
v8::HandleScope handle_scope(args->isolate());
v8::Local<v8::Function> callback;
if (!args->GetNext(&callback)) {
args->ThrowError();
return;
}
auto global_callback =
std::make_unique<v8::Global<v8::Function>>(args->isolate(), callback);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&SandboxStatusExtension::ReadSandboxStatus, this),
base::BindOnce(&SandboxStatusExtension::RunCallback, this,
std::move(global_callback)));
}
base::Value::Dict SandboxStatusExtension::ReadSandboxStatus() {
std::string secontext;
base::FilePath path(FILE_PATH_LITERAL("/proc/self/attr/current"));
base::ReadFileToString(path, &secontext);
std::string proc_status;
path = base::FilePath(FILE_PATH_LITERAL("/proc/self/status"));
base::ReadFileToString(path, &proc_status);
base::Value::Dict status;
status.Set("uid", static_cast<int>(getuid()));
status.Set("pid", getpid());
status.Set("secontext", secontext);
status.Set("seccompStatus",
static_cast<int>(content::GetSeccompSandboxStatus()));
status.Set("procStatus", proc_status);
status.Set("androidBuildId",
base::android::BuildInfo::GetInstance()->android_build_id());
return status;
}
void SandboxStatusExtension::RunCallback(
std::unique_ptr<v8::Global<v8::Function>> callback,
base::Value::Dict status) {
if (!render_frame())
return;
blink::WebLocalFrame* web_frame = render_frame()->GetWebFrame();
v8::Isolate* isolate = web_frame->GetAgentGroupScheduler()->Isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = web_frame->MainWorldScriptContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::Function> callback_local =
v8::Local<v8::Function>::New(isolate, *callback);
v8::Local<v8::Value> argv[] = {
content::V8ValueConverter::Create()->ToV8Value(status, context)};
web_frame->CallFunctionEvenIfScriptDisabled(
callback_local, v8::Object::New(isolate), 1, argv);
}