Update from chromium 62675d9fb31fb8cedc40f68e78e8445a74f362e7
This is Cr-Commit-Position: refs/heads/master@{#300999}
Review URL: https://codereview.chromium.org/670183003
diff --git a/DEPS b/DEPS
index b9d9875..7b6bdd8 100644
--- a/DEPS
+++ b/DEPS
@@ -30,12 +30,12 @@
'skia_git': 'https://skia.googlesource.com',
'boringssl_git': 'https://boringssl.googlesource.com',
'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
- 'skia_revision': '22b5ab644e0381e672c6fcf8aff26bf82ee71989',
+ 'skia_revision': '14e4d392d4ffa50c0ddf359a426668d1718d7f92',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling Skia
# and V8 without interference from each other.
'v8_branch': 'trunk',
- 'v8_revision': 'b404893cc89fe7419e614abccb1fddf69d308abc', # from svn revision 24791
+ 'v8_revision': '26db57054e04a5b35e7ad88aecb3e05895491a5c', # from svn revision 24827
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling ANGLE
# and whatever else without interference from each other.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 669d739..556a09a 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1421,8 +1421,7 @@
# GYP: //base.gyp:base_java
android_library("base_java") {
srcjar_deps = [
- ":base_java_application_state",
- ":base_java_memory_pressure_level_list",
+ ":base_android_java_enums_srcjar",
":base_native_libraries_gen",
]
@@ -1458,27 +1457,16 @@
}
# GYP: //base.gyp:base_java_application_state
- java_cpp_template("base_java_application_state") {
+ # GYP: //base.gyp:base_java_memory_pressure_level
+ java_cpp_enum("base_android_java_enums_srcjar") {
sources = [
- "android/java/src/org/chromium/base/ApplicationState.template",
+ "android/application_status_listener.h",
+ "memory/memory_pressure_listener.h",
]
- inputs = [
- "android/application_state_list.h"
+ outputs = [
+ "org/chromium/base/ApplicationState.java",
+ "org/chromium/base/MemoryPressureLevel.java",
]
-
- package_name = "org/chromium/base"
- }
-
- # GYP: //base.gyp:base_java_memory_pressure_level_list
- java_cpp_template("base_java_memory_pressure_level_list") {
- sources = [
- "android/java/src/org/chromium/base/MemoryPressureLevelList.template",
- ]
- inputs = [
- "memory/memory_pressure_level_list.h"
- ]
-
- package_name = "org/chromium/base"
}
# GYP: //base/base.gyp:base_native_libraries_gen
diff --git a/base/android/application_state_list.h b/base/android/application_state_list.h
deleted file mode 100644
index cbc833e..0000000
--- a/base/android/application_state_list.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file intentionally does not have header guards, it's included
-// inside a macro to generate enum values.
-
-// Note that these states represent the most visible Activity state.
-// If there are activities with states paused and stopped, only
-// HAS_PAUSED_ACTIVITIES should be returned.
-#ifndef DEFINE_APPLICATION_STATE
-#error "DEFINE_APPLICATION_STATE should be defined before including this file"
-#endif
-DEFINE_APPLICATION_STATE(HAS_RUNNING_ACTIVITIES, 1)
-DEFINE_APPLICATION_STATE(HAS_PAUSED_ACTIVITIES, 2)
-DEFINE_APPLICATION_STATE(HAS_STOPPED_ACTIVITIES, 3)
-DEFINE_APPLICATION_STATE(HAS_DESTROYED_ACTIVITIES, 4)
diff --git a/base/android/application_status_listener.h b/base/android/application_status_listener.h
index ef98985..30048b2 100644
--- a/base/android/application_status_listener.h
+++ b/base/android/application_status_listener.h
@@ -19,10 +19,18 @@
// Define application state values like APPLICATION_STATE_VISIBLE in a
// way that ensures they're always the same than their Java counterpart.
+//
+// Note that these states represent the most visible Activity state.
+// If there are activities with states paused and stopped, only
+// HAS_PAUSED_ACTIVITIES should be returned.
+//
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
enum ApplicationState {
-#define DEFINE_APPLICATION_STATE(x, y) APPLICATION_STATE_##x = y,
-#include "base/android/application_state_list.h"
-#undef DEFINE_APPLICATION_STATE
+ APPLICATION_STATE_HAS_RUNNING_ACTIVITIES = 1,
+ APPLICATION_STATE_HAS_PAUSED_ACTIVITIES = 2,
+ APPLICATION_STATE_HAS_STOPPED_ACTIVITIES = 3,
+ APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES = 4
};
// A native helper class to listen to state changes of the Android
diff --git a/base/android/java/src/org/chromium/base/ApplicationState.template b/base/android/java/src/org/chromium/base/ApplicationState.template
deleted file mode 100644
index 892c055..0000000
--- a/base/android/java/src/org/chromium/base/ApplicationState.template
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-// A simple auto-generated interface used to list the various
-// states of an application as used by both org.chromium.base.ApplicationStatus
-// and base/android/application_status.h
-public interface ApplicationState {
-#define DEFINE_APPLICATION_STATE(x,y) public final int x = y;
-#include "base/android/application_state_list.h"
-#undef DEFINE_APPLICATION_STATE
-}
diff --git a/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template b/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template
deleted file mode 100644
index cebca84..0000000
--- a/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base;
-
-class MemoryPressureLevelList {
-#define DEFINE_MEMORY_PRESSURE_LEVEL(name, value) \
- static final int name = value;
-#include "base/memory/memory_pressure_level_list.h"
-#undef DEFINE_MEMORY_PRESSURE_LEVEL
-}
diff --git a/base/android/java/src/org/chromium/base/MemoryPressureListener.java b/base/android/java/src/org/chromium/base/MemoryPressureListener.java
index 1f3b77f..28d9651 100644
--- a/base/android/java/src/org/chromium/base/MemoryPressureListener.java
+++ b/base/android/java/src/org/chromium/base/MemoryPressureListener.java
@@ -53,7 +53,7 @@
@Override
public void onLowMemory() {
- nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
+ nativeOnMemoryPressure(MemoryPressureLevel.CRITICAL);
}
@Override
@@ -85,12 +85,12 @@
public static void maybeNotifyMemoryPresure(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
- nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
+ nativeOnMemoryPressure(MemoryPressureLevel.CRITICAL);
} else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND ||
level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
// Don't notifiy on TRIM_MEMORY_UI_HIDDEN, since this class only
// dispatches actionable memory pressure signals to native.
- nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_MODERATE);
+ nativeOnMemoryPressure(MemoryPressureLevel.MODERATE);
}
}
diff --git a/base/android/BUILD.gn b/base/android/linker/BUILD.gn
similarity index 65%
rename from base/android/BUILD.gn
rename to base/android/linker/BUILD.gn
index befe397..b26e3b0 100644
--- a/base/android/BUILD.gn
+++ b/base/android/linker/BUILD.gn
@@ -1,10 +1,15 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
import("//build/config/android/config.gni")
assert(is_android)
assert(!is_android_webview_build)
+# GYP: //base/base.gyp:chromium_android_linker
shared_library("chromium_android_linker") {
- sources = [ "linker/linker_jni.cc" ]
+ sources = [ "linker_jni.cc" ]
# The NDK contains the crazy_linker here:
# '<(android_ndk_root)/crazy_linker.gyp:crazy_linker'
# However, we use our own fork. See bug 384700.
diff --git a/base/android/linker/config.gni b/base/android/linker/config.gni
new file mode 100644
index 0000000..99cbcf0
--- /dev/null
+++ b/base/android/linker/config.gni
@@ -0,0 +1,8 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(GYP) add "|| profiling_full_stack_frames
+# Only enable the chromium linker on regular builds, since the
+# component build crashes on Android 4.4. See b/11379966
+chromium_linker_supported = !is_component_build
diff --git a/base/base.gyp b/base/base.gyp
index 1a4b54e..1f801c8 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -1343,7 +1343,7 @@
},
'dependencies': [
'base_java_application_state',
- 'base_java_memory_pressure_level_list',
+ 'base_java_memory_pressure_level',
'base_native_libraries_gen',
],
'includes': [ '../build/java.gypi' ],
@@ -1368,34 +1368,22 @@
'includes': [ '../build/java.gypi' ],
},
{
- # GN: //base:base_java_application_state
+ # GN: //base:base_android_java_enums_srcjar
'target_name': 'base_java_application_state',
'type': 'none',
- # This target is used to auto-generate ApplicationState.java
- # from a template file. The source file contains a list of
- # Java constant declarations matching the ones in
- # android/application_state_list.h.
- 'sources': [
- 'android/java/src/org/chromium/base/ApplicationState.template',
- ],
'variables': {
- 'package_name': 'org/chromium/base',
- 'template_deps': ['android/application_state_list.h'],
+ 'source_file': 'android/application_status_listener.h',
},
- 'includes': [ '../build/android/java_cpp_template.gypi' ],
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
},
{
- # GN: //base:base_java_memory_pressure_level_list
- 'target_name': 'base_java_memory_pressure_level_list',
+ # GN: //base:base_android_java_enums_srcjar
+ 'target_name': 'base_java_memory_pressure_level',
'type': 'none',
- 'sources': [
- 'android/java/src/org/chromium/base/MemoryPressureLevelList.template',
- ],
'variables': {
- 'package_name': 'org/chromium/base',
- 'template_deps': ['memory/memory_pressure_level_list.h'],
+ 'source_file': 'memory/memory_pressure_listener.h',
},
- 'includes': [ '../build/android/java_cpp_template.gypi' ],
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
},
{
# GN: //base:base_java_test_support
@@ -1423,7 +1411,7 @@
'includes': [ '../build/java.gypi' ],
},
{
- # GN: //base/android/chromium_android_linker
+ # GN: //base/android/linker:chromium_android_linker
'target_name': 'chromium_android_linker',
'type': 'shared_library',
'conditions': [
diff --git a/base/base_nacl.gyp b/base/base_nacl.gyp
index 7221d2a..63e1ed4 100644
--- a/base/base_nacl.gyp
+++ b/base/base_nacl.gyp
@@ -36,7 +36,7 @@
],
},
'dependencies': [
- '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
+ '../native_client/tools.gyp:prep_toolchain',
],
},
{
@@ -59,22 +59,15 @@
],
},
'dependencies': [
- '<(DEPTH)/third_party/icu/icu_nacl.gyp:icudata_nacl',
- '<(DEPTH)/third_party/icu/icu_nacl.gyp:icui18n_nacl',
- '<(DEPTH)/third_party/icu/icu_nacl.gyp:icuuc_nacl',
- '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
+ '../native_client/tools.gyp:prep_toolchain',
+ '../third_party/icu/icu_nacl.gyp:icudata_nacl',
+ '../third_party/icu/icu_nacl.gyp:icui18n_nacl',
+ '../third_party/icu/icu_nacl.gyp:icuuc_nacl',
],
},
- ],
- }],
- ['disable_nacl==0', {
- 'targets': [
{
'target_name': 'base_nacl_nonsfi',
'type': 'none',
- 'include_dirs': [
- '<(DEPTH)/native_client/src/public/linux_syscalls',
- ],
'variables': {
'base_target': 1,
'nacl_untrusted_build': 1,
@@ -116,8 +109,8 @@
'rand_util_nacl.cc',
],
'dependencies': [
- '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
- '<(DEPTH)/third_party/libevent/libevent_nacl_nonsfi.gyp:event_nacl_nonsfi',
+ '../native_client/tools.gyp:prep_toolchain',
+ '../third_party/libevent/libevent_nacl_nonsfi.gyp:event_nacl_nonsfi',
],
},
],
diff --git a/base/logging.h b/base/logging.h
index 88d5eff..54ff579 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -450,14 +450,36 @@
#else
+#if defined(_PREFAST_) && defined(OS_WIN)
+// Use __analysis_assume to tell the VC++ static analysis engine that
+// assert conditions are true, to suppress warnings. The LAZY_STREAM
+// parameter doesn't reference 'condition' in /analyze builds because
+// this evaluation confuses /analyze. The !! before condition is because
+// __analysis_assume gets confused on some conditions:
+// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/
+
+#define CHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(LOG_STREAM(FATAL), false) \
+ << "Check failed: " #condition ". "
+
+#define PCHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(PLOG_STREAM(FATAL), false) \
+ << "Check failed: " #condition ". "
+
+#else // _PREFAST_
+
#define CHECK(condition) \
LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \
<< "Check failed: " #condition ". "
-#define PCHECK(condition) \
+#define PCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
<< "Check failed: " #condition ". "
+#endif // _PREFAST_
+
// Helper macro for binary operators.
// Don't use this macro directly in your code, use CHECK_EQ et al below.
//
@@ -616,6 +638,21 @@
// variable warnings if the only use of a variable is in a DCHECK.
// This behavior is different from DLOG_IF et al.
+#if defined(_PREFAST_) && defined(OS_WIN)
+// See comments on the previous use of __analysis_assume.
+
+#define DCHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(LOG_STREAM(DCHECK), false) \
+ << "Check failed: " #condition ". "
+
+#define DPCHECK(condition) \
+ __analysis_assume(!!(condition)), \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
+ << "Check failed: " #condition ". "
+
+#else // _PREFAST_
+
#define DCHECK(condition) \
LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON ? !(condition) : false) \
<< "Check failed: " #condition ". "
@@ -624,6 +661,8 @@
LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON ? !(condition) : false) \
<< "Check failed: " #condition ". "
+#endif // _PREFAST_
+
// Helper macro for binary operators.
// Don't use this macro directly in your code, use DCHECK_EQ et al below.
#define DCHECK_OP(name, op, val1, val2) \
diff --git a/base/memory/memory_pressure_level_list.h b/base/memory/memory_pressure_level_list.h
deleted file mode 100644
index bf3ce60..0000000
--- a/base/memory/memory_pressure_level_list.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file intentionally does not have header guards, it's included
-// inside a macro to generate enum and a java class for the values.
-
-#ifndef DEFINE_MEMORY_PRESSURE_LEVEL
-#error "DEFINE_MEMORY_PRESSURE_LEVEL should be defined."
-#endif
-
-// Modules are advised to free buffers that are cheap to re-allocate and not
-// immediately needed.
-DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_MODERATE, 0)
-
-// At this level, modules are advised to free all possible memory.
-// The alternative is to be killed by the system, which means all memory will
-// have to be re-created, plus the cost of a cold start.
-DEFINE_MEMORY_PRESSURE_LEVEL(MEMORY_PRESSURE_CRITICAL, 2)
diff --git a/base/memory/memory_pressure_listener.h b/base/memory/memory_pressure_listener.h
index 90eb144..f586209 100644
--- a/base/memory/memory_pressure_listener.h
+++ b/base/memory/memory_pressure_listener.h
@@ -49,10 +49,17 @@
//
class BASE_EXPORT MemoryPressureListener {
public:
+ // A Java counterpart will be generated for this enum.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
enum MemoryPressureLevel {
-#define DEFINE_MEMORY_PRESSURE_LEVEL(name, value) name = value,
-#include "base/memory/memory_pressure_level_list.h"
-#undef DEFINE_MEMORY_PRESSURE_LEVEL
+ // Modules are advised to free buffers that are cheap to re-allocate and not
+ // immediately needed.
+ MEMORY_PRESSURE_LEVEL_MODERATE = 0,
+
+ // At this level, modules are advised to free all possible memory. The
+ // alternative is to be killed by the system, which means all memory will
+ // have to be re-created, plus the cost of a cold start.
+ MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
};
typedef base::Callback<void(MemoryPressureLevel)> MemoryPressureCallback;
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 05afb9b..c82f33d 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -320,8 +320,11 @@
double entropy_value;
if (randomization_type == FieldTrial::ONE_TIME_RANDOMIZED) {
- entropy_value = GetEntropyProviderForOneTimeRandomization()->
- GetEntropyForTrial(trial_name, randomization_seed);
+ const FieldTrial::EntropyProvider* entropy_provider =
+ GetEntropyProviderForOneTimeRandomization();
+ CHECK(entropy_provider);
+ entropy_value = entropy_provider->GetEntropyForTrial(trial_name,
+ randomization_seed);
} else {
DCHECK_EQ(FieldTrial::SESSION_RANDOMIZED, randomization_type);
DCHECK_EQ(0U, randomization_seed);
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 905cc22..80c729d 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -990,4 +990,15 @@
}
}
+#if GTEST_HAS_DEATH_TEST
+TEST(FieldTrialDeathTest, OneTimeRandomizedTrialWithoutFieldTrialList) {
+ // Trying to instantiate a one-time randomized field trial before the
+ // FieldTrialList is created should crash.
+ EXPECT_DEATH(FieldTrialList::FactoryGetFieldTrial(
+ "OneTimeRandomizedTrialWithoutFieldTrialList", 100, kDefaultGroupName,
+ base::FieldTrialList::kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, NULL), "");
+}
+#endif
+
} // namespace base
diff --git a/base/win/metro.cc b/base/win/metro.cc
index 62743c7..6e443ba 100644
--- a/base/win/metro.cc
+++ b/base/win/metro.cc
@@ -74,8 +74,8 @@
}
bool IsParentalControlActivityLoggingOn() {
- // Query this info on Windows Vista and above.
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ // Query this info on Windows 7 and above.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
return false;
static bool parental_control_logging_required = false;
diff --git a/build/all.gyp b/build/all.gyp
index b469a22..f421983 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -98,7 +98,6 @@
['OS!="ios" and OS!="android"', {
'dependencies': [
'../third_party/re2/re2.gyp:re2',
- '../chrome/chrome.gyp:*',
'../chrome/tools/profile_reset/jtl_compiler.gyp:*',
'../cc/blink/cc_blink_tests.gyp:*',
'../cc/cc_tests.gyp:*',
@@ -143,6 +142,21 @@
'../v8/tools/gyp/v8.gyp:*',
'<(libjpeg_gyp_path):*',
],
+ 'conditions': [
+ ['use_athena==1' , {
+ 'dependencies': [
+ # Athena temporarily depends upon a subset of chrome. Since most
+ # tests do not compile, we only include dependencies to tests we
+ # want to build.
+ '../chrome/chrome.gyp:chrome',
+ '../chrome/chrome.gyp:browser_tests',
+ ]
+ }, {
+ 'dependencies': [
+ '../chrome/chrome.gyp:*',
+ ],
+ }],
+ ],
}],
['OS=="mac" or OS=="ios" or OS=="win"', {
'dependencies': [
@@ -305,8 +319,6 @@
'../chrome/chrome.gyp:chromedriver_tests',
'../chrome/chrome.gyp:chromedriver_unittests',
'../chrome/chrome.gyp:interactive_ui_tests',
- '../chrome/chrome.gyp:sync_integration_tests',
- '../chrome/chrome.gyp:unit_tests',
'../cloud_print/cloud_print.gyp:cloud_print_unittests',
'../content/content_shell_and_tests.gyp:content_browsertests',
'../content/content_shell_and_tests.gyp:content_shell',
@@ -332,6 +344,14 @@
'../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests',
'../tools/telemetry/telemetry.gyp:*',
],
+ 'conditions': [
+ ['use_athena!=1', {
+ 'dependencies' : [
+ '../chrome/chrome.gyp:sync_integration_tests',
+ '../chrome/chrome.gyp:unit_tests',
+ ],
+ }],
+ ],
}],
['OS=="win"', {
'dependencies': [
@@ -1211,8 +1231,6 @@
'../cc/cc_tests.gyp:cc_unittests',
'../chrome/chrome.gyp:browser_tests',
'../chrome/chrome.gyp:chrome',
- '../chrome/chrome.gyp:interactive_ui_tests',
- '../chrome/chrome.gyp:unit_tests',
'../components/components_tests.gyp:components_unittests',
'../content/content_shell_and_tests.gyp:content_browsertests',
'../content/content_shell_and_tests.gyp:content_unittests',
@@ -1238,6 +1256,12 @@
'blink_tests',
],
'conditions': [
+ ['use_athena!=1', {
+ 'dependencies': [
+ '../chrome/chrome.gyp:interactive_ui_tests',
+ '../chrome/chrome.gyp:unit_tests',
+ ],
+ }],
['OS=="win"', {
'dependencies': [
'../chrome/chrome.gyp:crash_service',
@@ -1302,13 +1326,19 @@
'dependencies': [
'../base/base.gyp:base_unittests_run',
'../chrome/chrome.gyp:browser_tests_run',
- '../chrome/chrome.gyp:interactive_ui_tests_run',
- '../chrome/chrome.gyp:sync_integration_tests_run',
- '../chrome/chrome.gyp:unit_tests_run',
'../content/content_shell_and_tests.gyp:content_browsertests_run',
'../content/content_shell_and_tests.gyp:content_unittests_run',
'../net/net.gyp:net_unittests_run',
],
+ 'conditions' : [
+ ['use_athena!=1', {
+ 'dependencies': [
+ '../chrome/chrome.gyp:interactive_ui_tests_run',
+ '../chrome/chrome.gyp:sync_integration_tests_run',
+ '../chrome/chrome.gyp:unit_tests_run',
+ ],
+ }],
+ ],
}, # target_name: chromium_swarm_tests
],
}],
diff --git a/build/android/adb_run_android_webview_shell b/build/android/adb_run_android_webview_shell
index cc9f6d2..1014a73 100755
--- a/build/android/adb_run_android_webview_shell
+++ b/build/android/adb_run_android_webview_shell
@@ -4,12 +4,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-if [ $# -gt 0 ] ; then
- INTENT_ARGS="-d \"$1\"" # e.g. a URL
-fi
+optional_url=$1
adb shell am start \
-a android.intent.action.VIEW \
-n org.chromium.android_webview.shell/.AwShellActivity \
- $INTENT_ARGS
-
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_chrome_shell b/build/android/adb_run_chrome_shell
index 46558b3..79c4c32 100755
--- a/build/android/adb_run_chrome_shell
+++ b/build/android/adb_run_chrome_shell
@@ -4,11 +4,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-if [ $# -gt 0 ] ; then
- INTENT_ARGS="-d \"$1\"" # e.g. a URL
-fi
+optional_url=$1
adb shell am start \
-a android.intent.action.VIEW \
-n org.chromium.chrome.shell/.ChromeShellActivity \
- $INTENT_ARGS
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_content_shell b/build/android/adb_run_content_shell
index 17a734c..3f01f3b 100755
--- a/build/android/adb_run_content_shell
+++ b/build/android/adb_run_content_shell
@@ -4,11 +4,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-if [ $# -gt 0 ] ; then
- INTENT_ARGS="-d \"$1\"" # e.g. a URL
-fi
+optional_url=$1
adb shell am start \
-a android.intent.action.VIEW \
-n org.chromium.content_shell_apk/.ContentShellActivity \
- $INTENT_ARGS
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_mojo_shell b/build/android/adb_run_mojo_shell
index 6f55fb2..18e796d 100755
--- a/build/android/adb_run_mojo_shell
+++ b/build/android/adb_run_mojo_shell
@@ -4,13 +4,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-if [ $# -gt 0 ] ; then
- INTENT_ARGS="-d \"$1\"" # e.g. a URL
-fi
+optional_url=$1
adb logcat -c
adb shell am start -S \
-a android.intent.action.VIEW \
-n org.chromium.mojo_shell_apk/.MojoShellActivity \
- $INTENT_ARGS
+ ${optional_url:+-d "$optional_url"}
adb logcat -s MojoShellApplication MojoShellActivity chromium
diff --git a/build/android/buildbot/bb_device_status_check.py b/build/android/buildbot/bb_device_status_check.py
index 6d736a1..d98c1dd 100755
--- a/build/android/buildbot/bb_device_status_check.py
+++ b/build/android/buildbot/bb_device_status_check.py
@@ -31,7 +31,6 @@
from pylib import constants
from pylib.cmd_helper import GetCmdOutput
from pylib.device import device_blacklist
-from pylib.device import device_errors
from pylib.device import device_list
from pylib.device import device_utils
@@ -83,8 +82,13 @@
errors = []
dev_good = True
if battery_level < 15:
- errors += ['Device critically low in battery. Turning off device.']
+ errors += ['Device critically low in battery. Will add to blacklist.']
dev_good = False
+ if not device_adb.old_interface.IsDeviceCharging():
+ if device_adb.old_interface.CanControlUsbCharging():
+ device_adb.old_interface.EnableUsbCharging()
+ else:
+ logging.error('Device %s is not charging' % serial)
if not options.no_provisioning_check:
setup_wizard_disabled = (
device_adb.GetProp('ro.setupwizard.mode') == 'DISABLED')
@@ -94,16 +98,6 @@
battery_info.get('AC powered', None) != 'true'):
errors += ['Mantaray device not connected to AC power.']
- # Turn off devices with low battery.
- if battery_level < 15:
- try:
- device_adb.EnableRoot()
- except device_errors.CommandFailedError as e:
- # Attempt shutdown anyway.
- # TODO(jbudorick) Handle this exception appropriately after interface
- # conversions are finished.
- logging.error(str(e))
- device_adb.old_interface.Shutdown()
full_report = '\n'.join(report)
return device_type, device_build, battery_level, full_report, errors, dev_good
@@ -377,10 +371,13 @@
'unique_builds': unique_builds,
}))
- if False in fail_step_lst:
- # TODO(navabi): Build fails on device status check step if there exists any
- # devices with critically low battery. Remove those devices from testing,
- # allowing build to continue with good devices.
+ num_failed_devs = 0
+ for fail_status, device in zip(fail_step_lst, devices):
+ if not fail_status:
+ device_blacklist.ExtendBlacklist([str(device)])
+ num_failed_devs += 1
+
+ if num_failed_devs == len(devices):
return 2
if not devices:
diff --git a/build/android/disable_lto.gypi b/build/android/disable_lto.gypi
new file mode 100644
index 0000000..448945c
--- /dev/null
+++ b/build/android/disable_lto.gypi
@@ -0,0 +1,20 @@
+# Copyright (c) 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This file is meant to be included to disable LTO on a target.
+
+{
+ 'target_conditions': [
+ ['_toolset=="target"', {
+ 'conditions': [
+ ['use_lto==1 or use_lto_o2==1', {
+ 'cflags!': [
+ '-flto',
+ '-ffat-lto-objects',
+ ],
+ }],
+ ],
+ }],
+ ],
+}
diff --git a/build/android/gyp/java_cpp_enum.py b/build/android/gyp/java_cpp_enum.py
index 6a1d5c1..8ae5f36 100755
--- a/build/android/gyp/java_cpp_enum.py
+++ b/build/android/gyp/java_cpp_enum.py
@@ -14,17 +14,23 @@
from util import build_utils
class EnumDefinition(object):
- def __init__(self, class_name=None, class_package=None, entries=None):
- self.class_name = class_name
- self.class_package = class_package
+ def __init__(self, original_enum_name=None, class_name_override=None,
+ enum_package=None, entries=None):
+ self.original_enum_name = original_enum_name
+ self.class_name_override = class_name_override
+ self.enum_package = enum_package
self.entries = collections.OrderedDict(entries or [])
- self.prefix_to_strip = ''
+ self.prefix_to_strip = None
def AppendEntry(self, key, value):
if key in self.entries:
raise Exception('Multiple definitions of key %s found.' % key)
self.entries[key] = value
+ @property
+ def class_name(self):
+ return self.class_name_override or self.original_enum_name
+
def Finalize(self):
self._Validate()
self._AssignEntryIndices()
@@ -32,7 +38,7 @@
def _Validate(self):
assert self.class_name
- assert self.class_package
+ assert self.enum_package
assert self.entries
def _AssignEntryIndices(self):
@@ -54,23 +60,59 @@
def _StripPrefix(self):
- if not self.prefix_to_strip:
- prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', self.class_name).upper()
+ prefix_to_strip = self.prefix_to_strip
+ if not prefix_to_strip:
+ prefix_to_strip = self.original_enum_name
+ prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', prefix_to_strip).upper()
prefix_to_strip += '_'
if not all([w.startswith(prefix_to_strip) for w in self.entries.keys()]):
prefix_to_strip = ''
- else:
- prefix_to_strip = self.prefix_to_strip
- entries = ((k.replace(prefix_to_strip, '', 1), v) for (k, v) in
- self.entries.iteritems())
- self.entries = collections.OrderedDict(entries)
+
+ entries = collections.OrderedDict()
+ for (k, v) in self.entries.iteritems():
+ stripped_key = k.replace(prefix_to_strip, '', 1)
+ if isinstance(v, basestring):
+ stripped_value = v.replace(prefix_to_strip, '', 1)
+ else:
+ stripped_value = v
+ entries[stripped_key] = stripped_value
+
+ self.entries = entries
+
+class DirectiveSet(object):
+ class_name_override_key = 'CLASS_NAME_OVERRIDE'
+ enum_package_key = 'ENUM_PACKAGE'
+ prefix_to_strip_key = 'PREFIX_TO_STRIP'
+
+ known_keys = [class_name_override_key, enum_package_key, prefix_to_strip_key]
+
+ def __init__(self):
+ self._directives = {}
+
+ def Update(self, key, value):
+ if key not in DirectiveSet.known_keys:
+ raise Exception("Unknown directive: " + key)
+ self._directives[key] = value
+
+ @property
+ def empty(self):
+ return len(self._directives) == 0
+
+ def UpdateDefinition(self, definition):
+ definition.class_name_override = self._directives.get(
+ DirectiveSet.class_name_override_key, '')
+ definition.enum_package = self._directives.get(
+ DirectiveSet.enum_package_key)
+ definition.prefix_to_strip = self._directives.get(
+ DirectiveSet.prefix_to_strip_key)
+
class HeaderParser(object):
single_line_comment_re = re.compile(r'\s*//')
multi_line_comment_start_re = re.compile(r'\s*/\*')
enum_start_re = re.compile(r'^\s*enum\s+(\w+)\s+{\s*$')
enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?')
- enum_end_re = re.compile(r'^\s*}\s*;\s*$')
+ enum_end_re = re.compile(r'^\s*}\s*;\.*$')
generator_directive_re = re.compile(
r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$')
@@ -79,7 +121,7 @@
self._enum_definitions = []
self._in_enum = False
self._current_definition = None
- self._generator_directives = {}
+ self._generator_directives = DirectiveSet()
def ParseDefinitions(self):
for line in self._lines:
@@ -109,31 +151,23 @@
enum_value = enum_entry.groups()[2]
self._current_definition.AppendEntry(enum_key, enum_value)
- def _GetCurrentEnumPackageName(self):
- return self._generator_directives.get('ENUM_PACKAGE')
-
- def _GetCurrentEnumPrefixToStrip(self):
- return self._generator_directives.get('PREFIX_TO_STRIP', '')
-
def _ApplyGeneratorDirectives(self):
- current_definition = self._current_definition
- current_definition.class_package = self._GetCurrentEnumPackageName()
- current_definition.prefix_to_strip = self._GetCurrentEnumPrefixToStrip()
- self._generator_directives = {}
+ self._generator_directives.UpdateDefinition(self._current_definition)
+ self._generator_directives = DirectiveSet()
def _ParseRegularLine(self, line):
enum_start = HeaderParser.enum_start_re.match(line)
generator_directive = HeaderParser.generator_directive_re.match(line)
if enum_start:
- if not self._GetCurrentEnumPackageName():
+ if self._generator_directives.empty:
return
- self._current_definition = EnumDefinition()
- self._current_definition.class_name = enum_start.groups()[0]
+ self._current_definition = EnumDefinition(
+ original_enum_name=enum_start.groups()[0])
self._in_enum = True
elif generator_directive:
directive_name = generator_directive.groups()[0]
directive_value = generator_directive.groups()[1]
- self._generator_directives[directive_name] = directive_value
+ self._generator_directives.Update(directive_name, directive_value)
def GetScriptName():
@@ -147,7 +181,7 @@
for source_path in source_paths:
enum_definitions = DoParseHeaderFile(source_path)
for enum_definition in enum_definitions:
- package_path = enum_definition.class_package.replace('.', os.path.sep)
+ package_path = enum_definition.enum_package.replace('.', os.path.sep)
file_name = enum_definition.class_name + '.java'
output_path = os.path.join(options.output_dir, package_path, file_name)
output_paths.append(output_path)
@@ -193,7 +227,7 @@
values = {
'CLASS_NAME': enum_definition.class_name,
'ENUM_ENTRIES': enum_entries_string,
- 'PACKAGE': enum_definition.class_package,
+ 'PACKAGE': enum_definition.enum_package,
'SCRIPT_NAME': GetScriptName(),
'SOURCE_PATH': source_path,
}
diff --git a/build/android/gyp/java_cpp_enum_tests.py b/build/android/gyp/java_cpp_enum_tests.py
index 24da05f..bb8150d 100755
--- a/build/android/gyp/java_cpp_enum_tests.py
+++ b/build/android/gyp/java_cpp_enum_tests.py
@@ -9,13 +9,20 @@
"""
import collections
+import optparse
+import os
+import sys
import unittest
+
from java_cpp_enum import EnumDefinition, GenerateOutput, HeaderParser
+sys.path.append(os.path.join(os.path.dirname(__file__), "gyp"))
+from util import build_utils
+
class TestPreprocess(unittest.TestCase):
def testOutput(self):
- definition = EnumDefinition(class_name='ClassName',
- class_package='some.package',
+ definition = EnumDefinition(original_enum_name='ClassName',
+ enum_package='some.package',
entries=[('E1', 1), ('E2', '2 << 2')])
output = GenerateOutput('path/to/file', definition)
expected = """
@@ -49,11 +56,54 @@
self.assertEqual(1, len(definitions))
definition = definitions[0]
self.assertEqual('EnumName', definition.class_name)
- self.assertEqual('test.namespace', definition.class_package)
+ self.assertEqual('test.namespace', definition.enum_package)
self.assertEqual(collections.OrderedDict([('VALUE_ZERO', 0),
('VALUE_ONE', 1)]),
definition.entries)
+ def testParseBitShifts(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ enum EnumName {
+ VALUE_ZERO = 1 << 0,
+ VALUE_ONE = 1 << 1,
+ };
+ """.split('\n')
+ definitions = HeaderParser(test_data).ParseDefinitions()
+ self.assertEqual(1, len(definitions))
+ definition = definitions[0]
+ self.assertEqual('EnumName', definition.class_name)
+ self.assertEqual('test.namespace', definition.enum_package)
+ self.assertEqual(collections.OrderedDict([('VALUE_ZERO', '1 << 0'),
+ ('VALUE_ONE', '1 << 1')]),
+ definition.entries)
+
+ def testParseClassNameOverride(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OverrideName
+ enum EnumName {
+ FOO
+ };
+
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OtherOverride
+ enum PrefixTest {
+ PREFIX_TEST_A,
+ PREFIX_TEST_B,
+ };
+ """.split('\n')
+ definitions = HeaderParser(test_data).ParseDefinitions()
+ self.assertEqual(2, len(definitions))
+ definition = definitions[0]
+ self.assertEqual('OverrideName', definition.class_name)
+
+ definition = definitions[1]
+ self.assertEqual('OtherOverride', definition.class_name)
+ self.assertEqual(collections.OrderedDict([('A', 0),
+ ('B', 1)]),
+ definition.entries)
+
def testParseTwoEnums(self):
test_data = """
// GENERATED_JAVA_ENUM_PACKAGE: test.namespace
@@ -78,20 +128,30 @@
self.assertEqual(2, len(definitions))
definition = definitions[0]
self.assertEqual('EnumOne', definition.class_name)
- self.assertEqual('test.namespace', definition.class_package)
+ self.assertEqual('test.namespace', definition.enum_package)
self.assertEqual(collections.OrderedDict([('A', '1'),
('B', 'A')]),
definition.entries)
definition = definitions[1]
self.assertEqual('EnumTwo', definition.class_name)
- self.assertEqual('other.package', definition.class_package)
+ self.assertEqual('other.package', definition.enum_package)
self.assertEqual(collections.OrderedDict([('A', 0),
('B', 1)]),
definition.entries)
+ def testParseThrowsOnUnknownDirective(self):
+ test_data = """
+ // GENERATED_JAVA_UNKNOWN: Value
+ enum EnumName {
+ VALUE_ONE,
+ };
+ """.split('\n')
+ with self.assertRaises(Exception):
+ HeaderParser(test_data).ParseDefinitions()
+
def testEnumValueAssignmentNoneDefined(self):
- definition = EnumDefinition('c', 'p', [])
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
definition.AppendEntry('A', None)
definition.AppendEntry('B', None)
definition.AppendEntry('C', None)
@@ -102,7 +162,7 @@
definition.entries)
def testEnumValueAssignmentAllDefined(self):
- definition = EnumDefinition('c', 'p', [])
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
definition.AppendEntry('A', '1')
definition.AppendEntry('B', '2')
definition.AppendEntry('C', '3')
@@ -113,7 +173,7 @@
definition.entries)
def testEnumValueAssignmentReferences(self):
- definition = EnumDefinition('c', 'p', [])
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
definition.AppendEntry('A', None)
definition.AppendEntry('B', 'A')
definition.AppendEntry('C', None)
@@ -125,39 +185,85 @@
('D', 1)]),
definition.entries)
- def testEnumValueAssignmentRaises(self):
- definition = EnumDefinition('c', 'p', [])
+ def testEnumValueAssignmentSet(self):
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
definition.AppendEntry('A', None)
- definition.AppendEntry('B', '1')
+ definition.AppendEntry('B', '2')
+ definition.AppendEntry('C', None)
+ definition.Finalize()
+ self.assertEqual(collections.OrderedDict([('A', 0),
+ ('B', 2),
+ ('C', 3)]),
+ definition.entries)
+
+ def testEnumValueAssignmentSetReferences(self):
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
+ definition.AppendEntry('A', None)
+ definition.AppendEntry('B', 'A')
+ definition.AppendEntry('C', 'B')
+ definition.AppendEntry('D', None)
+ definition.Finalize()
+ self.assertEqual(collections.OrderedDict([('A', 0),
+ ('B', 0),
+ ('C', 0),
+ ('D', 1)]),
+ definition.entries)
+
+ def testEnumValueAssignmentRaises(self):
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
+ definition.AppendEntry('A', None)
+ definition.AppendEntry('B', 'foo')
definition.AppendEntry('C', None)
with self.assertRaises(Exception):
definition.Finalize()
def testExplicitPrefixStripping(self):
- definition = EnumDefinition('c', 'p', [])
+ definition = EnumDefinition(original_enum_name='c', enum_package='p')
definition.AppendEntry('P_A', None)
definition.AppendEntry('B', None)
definition.AppendEntry('P_C', None)
+ definition.AppendEntry('P_LAST', 'P_C')
definition.prefix_to_strip = 'P_'
definition.Finalize()
- self.assertEqual(['A', 'B', 'C'], definition.entries.keys())
+ self.assertEqual(collections.OrderedDict([('A', 0),
+ ('B', 1),
+ ('C', 2),
+ ('LAST', 2)]),
+ definition.entries)
def testImplicitPrefixStripping(self):
- definition = EnumDefinition('ClassName', 'p', [])
+ definition = EnumDefinition(original_enum_name='ClassName',
+ enum_package='p')
definition.AppendEntry('CLASS_NAME_A', None)
definition.AppendEntry('CLASS_NAME_B', None)
definition.AppendEntry('CLASS_NAME_C', None)
+ definition.AppendEntry('CLASS_NAME_LAST', 'CLASS_NAME_C')
definition.Finalize()
- self.assertEqual(['A', 'B', 'C'], definition.entries.keys())
+ self.assertEqual(collections.OrderedDict([('A', 0),
+ ('B', 1),
+ ('C', 2),
+ ('LAST', 2)]),
+ definition.entries)
def testImplicitPrefixStrippingRequiresAllConstantsToBePrefixed(self):
- definition = EnumDefinition('Name', 'p', [])
+ definition = EnumDefinition(original_enum_name='Name',
+ enum_package='p')
definition.AppendEntry('A', None)
definition.AppendEntry('B', None)
definition.AppendEntry('NAME_LAST', None)
definition.Finalize()
self.assertEqual(['A', 'B', 'NAME_LAST'], definition.entries.keys())
+def main(argv):
+ parser = optparse.OptionParser()
+ parser.add_option("--stamp", help="File to touch on success.")
+ options, _ = parser.parse_args(argv)
+
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestPreprocess)
+ unittest.TextTestRunner(verbosity=0).run(suite)
+
+ if options.stamp:
+ build_utils.Touch(options.stamp)
if __name__ == '__main__':
- unittest.main()
+ main(sys.argv[1:])
diff --git a/build/android/gyp/pack_arm_relocations.py b/build/android/gyp/pack_arm_relocations.py
index de131d2..149e340 100755
--- a/build/android/gyp/pack_arm_relocations.py
+++ b/build/android/gyp/pack_arm_relocations.py
@@ -35,9 +35,6 @@
has_relocations_with_addends,
library_path,
output_path):
- if not build_utils.IsTimeStale(output_path, [library_path]):
- return
-
# Select an appropriate name for the section we add.
if has_relocations_with_addends:
new_section = '.android.rela.dyn'
@@ -59,15 +56,17 @@
def CopyArmLibraryUnchanged(library_path, output_path):
- if not build_utils.IsTimeStale(output_path, [library_path]):
- return
-
shutil.copy(library_path, output_path)
def main(args):
args = build_utils.ExpandFileArgs(args)
parser = optparse.OptionParser()
+ build_utils.AddDepfileOption(parser)
+ parser.add_option('--clear-dir', action='store_true',
+ help='If set, the destination directory will be deleted '
+ 'before copying files to it. This is highly recommended to '
+ 'ensure that no stale files are left in the directory.')
parser.add_option('--configuration-name',
default='Release',
@@ -102,11 +101,15 @@
libraries = build_utils.ParseGypList(options.libraries)
+ if options.clear_dir:
+ build_utils.DeleteDirectory(options.packed_libraries_dir)
+
build_utils.MakeDirectory(options.packed_libraries_dir)
for library in libraries:
library_path = os.path.join(options.stripped_libraries_dir, library)
- output_path = os.path.join(options.packed_libraries_dir, library)
+ output_path = os.path.join(
+ options.packed_libraries_dir, os.path.basename(library))
if enable_packing and library not in exclude_packing_set:
PackArmLibraryRelocations(options.android_pack_relocations,
@@ -117,6 +120,11 @@
else:
CopyArmLibraryUnchanged(library_path, output_path)
+ if options.depfile:
+ build_utils.WriteDepfile(
+ options.depfile,
+ libraries + build_utils.GetPythonDependencies())
+
if options.stamp:
build_utils.Touch(options.stamp)
diff --git a/build/android/gyp/package_resources.py b/build/android/gyp/package_resources.py
index f34578f..6444ed9 100755
--- a/build/android/gyp/package_resources.py
+++ b/build/android/gyp/package_resources.py
@@ -126,6 +126,7 @@
'-I', android_jar,
'-F', options.apk_path,
+ '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN,
]
if options.no_compress:
diff --git a/build/android/gyp/process_resources.py b/build/android/gyp/process_resources.py
index 6f35388..6bf71f3 100755
--- a/build/android/gyp/process_resources.py
+++ b/build/android/gyp/process_resources.py
@@ -21,6 +21,7 @@
from util import build_utils
+
def ParseArgs(args):
"""Parses command line options.
@@ -220,7 +221,8 @@
'--auto-add-overlay',
'-I', android_jar,
'--output-text-symbols', gen_dir,
- '-J', gen_dir]
+ '-J', gen_dir,
+ '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN]
for d in input_resource_dirs:
package_command += ['-S', d]
@@ -258,7 +260,8 @@
aapt_cmd = [aapt,
'crunch',
'-C', crunch_dir,
- '-S', d]
+ '-S', d,
+ '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN]
build_utils.CheckOutput(aapt_cmd, stderr_filter=FilterCrunchStderr,
fail_func=DidCrunchFail)
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index e3a3525..a0cd7c1 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -22,6 +22,9 @@
os.pardir, os.pardir, os.pardir, os.pardir))
COLORAMA_ROOT = os.path.join(CHROMIUM_SRC,
'third_party', 'colorama', 'src')
+# aapt should ignore OWNERS files in addition the default ignore pattern.
+AAPT_IGNORE_PATTERN = ('!OWNERS:!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:' +
+ '!CVS:!thumbs.db:!picasa.ini:!*~')
@contextlib.contextmanager
diff --git a/build/android/increase_size_for_speed.gypi b/build/android/increase_size_for_speed.gypi
index f5f2d62..4081cd7 100644
--- a/build/android/increase_size_for_speed.gypi
+++ b/build/android/increase_size_for_speed.gypi
@@ -18,6 +18,22 @@
'cflags!': ['-Os'],
'cflags': ['-O2'],
}],
+ # Do not merge -Os and -O2 in LTO.
+ # LTO merges all optimization options at link-time. -O2 takes
+ # precedence over -Os. Avoid using LTO simultaneously
+ # on -Os and -O2 parts for that reason.
+ ['use_lto==1', {
+ 'cflags!': [
+ '-flto',
+ '-ffat-lto-objects',
+ ],
+ }],
+ ['use_lto_o2==1', {
+ 'cflags': [
+ '-flto',
+ '-ffat-lto-objects',
+ ],
+ }],
],
}],
],
diff --git a/build/android/java_cpp_template.gypi b/build/android/java_cpp_template.gypi
index 036f32c..3296659 100644
--- a/build/android/java_cpp_template.gypi
+++ b/build/android/java_cpp_template.gypi
@@ -6,6 +6,9 @@
# to generate Java source files from templates that are processed
# through the host C pre-processor.
#
+# NOTE: For generating Java conterparts to enums prefer using the java_cpp_enum
+# rule instead.
+#
# To use this, create a gyp target with the following form:
# {
# 'target_name': 'android_net_java_constants',
@@ -15,7 +18,7 @@
# ],
# 'variables': {
# 'package_name': 'org/chromium/net',
-# 'template_deps': ['net/base/certificate_mime_type_list.h'],
+# 'template_deps': ['base/net_error_list.h'],
# },
# 'includes': [ '../build/android/java_cpp_template.gypi' ],
# },
diff --git a/build/android/provision_devices.py b/build/android/provision_devices.py
index 468ef3f..9962f0a 100755
--- a/build/android/provision_devices.py
+++ b/build/android/provision_devices.py
@@ -190,9 +190,11 @@
battery_info.get('level', 0))
time.sleep(60)
battery_info = device.old_interface.GetBatteryInfo()
- device.RunShellCommand('date -u %f' % time.time(), as_root=True)
# TODO(jbudorick): Tune the timeout per OS version.
device.Reboot(True, timeout=600, retries=0)
+ device.RunShellCommand('date -s %s' % time.strftime('%Y%m%d.%H%M%S',
+ time.gmtime()),
+ as_root=True)
props = device.RunShellCommand('getprop')
for prop in props:
logging.info(' %s' % prop)
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py
index d324946..f54d498 100644
--- a/build/android/pylib/constants.py
+++ b/build/android/pylib/constants.py
@@ -146,7 +146,22 @@
SCREENSHOTS_DIR = os.path.join(DIR_SOURCE_ROOT, 'out_screenshots')
-ANDROID_SDK_VERSION = 21
+class ANDROID_SDK_VERSION_CODES(object):
+ """Android SDK version codes.
+
+ http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
+ """
+
+ ICE_CREAM_SANDWICH = 14
+ ICE_CREAM_SANDWICH_MR1 = 15
+ JELLY_BEAN = 16
+ JELLY_BEAN_MR1 = 17
+ JELLY_BEAN_MR2 = 18
+ KITKAT = 19
+ KITKAT_WATCH = 20
+ LOLLIPOP = 21
+
+ANDROID_SDK_VERSION = ANDROID_SDK_VERSION_CODES.LOLLIPOP
ANDROID_SDK_BUILD_TOOLS_VERSION = '21.0.0'
ANDROID_SDK_ROOT = os.path.join(DIR_SOURCE_ROOT,
'third_party/android_tools/sdk')
diff --git a/build/android/pylib/content_settings.py b/build/android/pylib/content_settings.py
index d222053..f00553f 100644
--- a/build/android/pylib/content_settings.py
+++ b/build/android/pylib/content_settings.py
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from pylib import constants
+
class ContentSettings(dict):
@@ -15,7 +17,7 @@
sdk_version_string = device.GetProp('ro.build.version.sdk')
try:
sdk_version = int(sdk_version_string)
- assert sdk_version >= 16, (
+ assert sdk_version >= constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN, (
'ContentSettings supported only on SDK 16 and later')
except ValueError:
assert False, ('Unknown SDK version %s' % sdk_version_string)
diff --git a/build/android/pylib/device_settings.py b/build/android/pylib/device_settings.py
index bc39b5d..4050694 100644
--- a/build/android/pylib/device_settings.py
+++ b/build/android/pylib/device_settings.py
@@ -4,6 +4,7 @@
import logging
+from pylib import constants
from pylib import content_settings
_LOCK_SCREEN_SETTINGS_PATH = '/data/system/locksettings.db'
@@ -32,7 +33,7 @@
device.GetProp('ro.build.version.sdk'))
return
- if sdk_version < 16:
+ if sdk_version < constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN:
logging.error('Skipping content settings configuration due to outdated sdk')
return
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 821fec1..aa0a2c0 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -487,6 +487,9 @@
timeout = (self._GetIndividualTestTimeoutSecs(test) *
self._GetIndividualTestTimeoutScale(test) *
self.tool.GetTimeoutScale())
+ if (self.device.GetProp('ro.build.version.sdk')
+ < constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN):
+ timeout *= 4
start_ms = 0
duration_ms = 0
diff --git a/build/android/rezip.gyp b/build/android/rezip.gyp
index 0dacffc..1115177 100644
--- a/build/android/rezip.gyp
+++ b/build/android/rezip.gyp
@@ -6,6 +6,7 @@
{
'targets': [
{
+ # GN: //build/android/rezip:rezip
'target_name': 'rezip_apk_jar',
'type': 'none',
'variables': {
diff --git a/build/android/rezip/BUILD.gn b/build/android/rezip/BUILD.gn
new file mode 100644
index 0000000..8b8f78e
--- /dev/null
+++ b/build/android/rezip/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+# GYP: //build/android/rezip.gyp:rezip_apk_jar
+java_library("rezip") {
+ jar_path = "$root_build_dir/lib.java/rezip_apk.jar"
+ DEPRECATED_java_in_dir = "."
+}
diff --git a/build/common.gypi b/build/common.gypi
index 8f9cb38..ad0261c 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -387,10 +387,13 @@
# -fsanitize=address only works with clang, but asan=1 implies clang=1
# See https://sites.google.com/a/chromium.org/dev/developers/testing/addresssanitizer
'asan%': 0,
+ 'asan_blacklist%': '<(PRODUCT_DIR)/../../tools/memory/asan/blacklist.txt',
# Enable coverage gathering instrumentation in ASan. This flag also
# controls coverage granularity (1 for function-level coverage, 2 for
# block-level coverage).
'asan_coverage%': 0,
+ # Enable intra-object-overflow detection in ASan (experimental).
+ 'asan_field_padding%': 0,
# Enable Chromium overrides of the default configurations for various
# dynamic tools (like ASan).
@@ -637,6 +640,16 @@
# compiler. Always do this by default.
'host_clang%': 1,
+ # Variables to control Link-Time Optimizations (LTO).
+ # Note: the variables must *not* be enabled at the same time.
+ # In this case LTO would 'merge' the optimization flags
+ # at link-time which would lead to all code be optimized with -O2.
+ # Enable LTO on the code compiled with -Os.
+ # See crbug.com/407544
+ 'use_lto%': 0,
+ # Enable LTO on code compiled with -O2.
+ 'use_lto_o2%': 0,
+
'conditions': [
# A flag for POSIX platforms
['OS=="win"', {
@@ -1110,7 +1123,9 @@
'clang_use_chrome_plugins%': '<(clang_use_chrome_plugins)',
'mac_want_real_dsym%': '<(mac_want_real_dsym)',
'asan%': '<(asan)',
+ 'asan_blacklist%': '<(asan_blacklist)',
'asan_coverage%': '<(asan_coverage)',
+ 'asan_field_padding%': '<(asan_field_padding)',
'use_sanitizer_options%': '<(use_sanitizer_options)',
'syzyasan%': '<(syzyasan)',
'syzygy_optimize%': '<(syzygy_optimize)',
@@ -1185,6 +1200,8 @@
'proprietary_codecs%': '<(proprietary_codecs)',
'use_goma%': '<(use_goma)',
'gomadir%': '<(gomadir)',
+ 'use_lto%': '<(use_lto)',
+ 'use_lto_o2%': '<(use_lto_o2)',
'video_hole%': '<(video_hole)',
'enable_load_completion_hacks%': '<(enable_load_completion_hacks)',
'support_pre_M6_history_database%': '<(support_pre_M6_history_database)',
@@ -2313,6 +2330,11 @@
}, {
'use_seccomp_bpf%': 0,
}],
+ # Set component build with LTO until all tests pass.
+ # This also reduces link time.
+ ['use_lto==1', {
+ 'component%': "shared_library",
+ }],
],
# older history files use fts2 instead of fts3
@@ -3755,6 +3777,13 @@
'cflags': [
'-march=<(arm_arch)',
],
+ 'conditions': [
+ ['use_lto==1 or use_lto_o2==1', {
+ 'ldflags': [
+ '-march=<(arm_arch)',
+ ],
+ }],
+ ],
}],
['clang==1 and OS!="android"', {
'cflags': [
@@ -3767,21 +3796,49 @@
'cflags': [
'-mtune=<(arm_tune)',
],
+ 'conditions': [
+ ['use_lto==1 or use_lto_o2==1', {
+ 'ldflags': [
+ '-mtune=<(arm_tune)',
+ ],
+ }],
+ ],
}],
['arm_fpu!=""', {
'cflags': [
'-mfpu=<(arm_fpu)',
],
+ 'conditions': [
+ ['use_lto==1 or use_lto_o2==1', {
+ 'ldflags': [
+ '-mfpu=<(arm_fpu)',
+ ],
+ }],
+ ],
}],
['arm_float_abi!=""', {
'cflags': [
'-mfloat-abi=<(arm_float_abi)',
],
+ 'conditions': [
+ ['use_lto==1 or use_lto_o2==1', {
+ 'ldflags': [
+ '-mfloat-abi=<(arm_float_abi)',
+ ],
+ }],
+ ],
}],
['arm_thumb==1', {
'cflags': [
'-mthumb',
],
+ 'conditions': [
+ ['use_lto==1 or use_lto_o2==1', {
+ 'ldflags': [
+ '-mthumb',
+ ],
+ }],
+ ],
}],
['OS=="android"', {
# Most of the following flags are derived from what Android
@@ -4060,6 +4117,7 @@
['_toolset=="target"', {
'cflags': [
'-fsanitize=address',
+ '-fsanitize-blacklist=<(asan_blacklist)',
],
'ldflags': [
'-fsanitize=address',
@@ -4121,6 +4179,15 @@
}],
],
}],
+ ['asan_field_padding!=0', {
+ 'target_conditions': [
+ ['_toolset=="target"', {
+ 'cflags': [
+ '-fsanitize-address-field-padding=<(asan_field_padding)',
+ ],
+ }],
+ ],
+ }],
['lsan==1', {
'target_conditions': [
['_toolset=="target"', {
@@ -5685,6 +5752,29 @@
['CXX.host_wrapper', '<(gomadir)/gomacc'],
],
}],
+ ['use_lto==1', {
+ 'target_defaults': {
+ 'target_conditions': [
+ ['_toolset=="target"', {
+ 'cflags': [
+ '-flto',
+ '-ffat-lto-objects',
+ ],
+ }],
+ ],
+ },
+ }],
+ ['use_lto==1 or use_lto_o2==1', {
+ 'target_defaults': {
+ 'target_conditions': [
+ ['_toolset=="target"', {
+ 'ldflags': [
+ '-flto=32',
+ ],
+ }],
+ ],
+ },
+ }],
],
'xcode_settings': {
# DON'T ADD ANYTHING NEW TO THIS BLOCK UNLESS YOU REALLY REALLY NEED IT!
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index e548f77..64abdf3 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -32,7 +32,6 @@
defines = [
"CHROMIUM_BUILD",
"ENABLE_EGLIMAGE=1",
- "ENABLE_BACKGROUND=1",
"V8_DEPRECATION_WARNINGS", # Don't use deprecated V8 APIs anywhere.
]
@@ -192,6 +191,9 @@
if (enable_hidpi) {
defines += [ "ENABLE_HIDPI=1" ]
}
+ if (proprietary_codecs) {
+ defines += [ "USE_PROPRIETARY_CODECS" ]
+ }
}
# Debug/release ----------------------------------------------------------------
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index d002ba2..50cf8ba 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -114,7 +114,9 @@
assert(false, "Need android libgcc support for your target arch.")
}
- android_readelf = "$android_toolchain_root/bin/$_binary_prefix-readelf"
+ android_tool_prefix = "$android_toolchain_root/bin/$_binary_prefix-"
+ android_readelf = "${android_tool_prefix}readelf"
+ android_objcopy = "${android_tool_prefix}objcopy"
android_gdbserver = "$android_ndk_root/prebuilt/$android_prebuilt_arch/gdbserver/gdbserver"
# stlport stuff --------------------------------------------------------------
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index abd9711..d9522ce 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -236,6 +236,7 @@
_keystore_path = invoker.keystore_path
_keystore_name = invoker.keystore_name
_keystore_password = invoker.keystore_password
+ _load_library_from_apk = invoker.load_library_from_apk
_deps = []
if (defined(invoker.deps)) {
@@ -271,7 +272,7 @@
script = "//build/android/gyp/package_resources.py"
depfile = "${target_gen_dir}/${target_name}.d"
- source_prereqs = [
+ inputs = [
_android_manifest,
_resources_zip,
]
@@ -302,7 +303,7 @@
depfile = "$target_gen_dir/$target_name.d"
- source_prereqs = [
+ inputs = [
_dex_path,
_resource_packaged_apk_path,
_ant_script
@@ -344,7 +345,7 @@
depfile = "$target_gen_dir/$target_name.d"
sources = [_packaged_apk_path]
- source_prereqs = [_keystore_path]
+ inputs = [_keystore_path]
outputs = [depfile, _final_apk_path]
args = [
@@ -356,6 +357,16 @@
"--key-name", _keystore_name,
"--key-passwd", _keystore_password,
]
+ if (_load_library_from_apk) {
+ _rezip_jar_path = "$root_build_dir/lib.java/rezip_apk.jar"
+ inputs += [
+ _rezip_jar_path
+ ]
+ args += [
+ "--load-library-from-zip-file=1",
+ "--rezip-apk-jar-path", rebase_path(_rezip_jar_path, root_build_dir)
+ ]
+ }
}
group(target_name) {
@@ -446,7 +457,7 @@
# dependencies srcjar outputs will be compiled and added to the output jar.
# jar_path: Use this to explicitly set the output jar path. Defaults to
# "${target_gen_dir}/${target_name}.jar.
-template("java_library") {
+template("compile_java") {
if (defined(invoker.testonly)) { testonly = invoker.testonly }
assert(defined(invoker.java_files))
@@ -598,7 +609,7 @@
_final_deps = []
_final_datadeps = []
- java_library("${target_name}__java_library") {
+ compile_java("${target_name}__compile_java") {
jar_path = _jar_path
if (defined(invoker.jar_excluded_patterns)) {
jar_excluded_patterns = invoker.jar_excluded_patterns
@@ -634,7 +645,7 @@
group(target_name) {
deps = [
- ":${target_name}__java_library",
+ ":${target_name}__compile_java",
":${target_name}__dex",
] + _final_deps + _final_datadeps
}
@@ -672,7 +683,7 @@
)
sources = rebase_path(sources_build_rel, ".", root_build_dir)
- source_prereqs = [
+ inputs = [
build_config,
android_manifest,
]
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index ac6fe5a..2db732a 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2,13 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//base/android/linker/config.gni")
import("//build/config/android/config.gni")
import("//build/config/android/internal_rules.gni")
import("//tools/grit/grit_rule.gni")
+import("//tools/relocation_packer/config.gni")
assert(is_android)
-
# Declare a jni target
#
# This target generates the native jni bindings for a set of .java files.
@@ -186,6 +187,9 @@
# Declare a target for c-preprocessor-generated java files
#
+# NOTE: For generating Java conterparts to enums prefer using the java_cpp_enum
+# rule instead.
+#
# This target generates java files using the host C pre-processor. Each file in
# sources will be compiled using the C pre-processor. If include_path is
# specified, it will be passed (with --I) to the pre-processor.
@@ -732,6 +736,68 @@
}
}
+template("java_library") {
+ if (defined(invoker.testonly)) { testonly = invoker.testonly }
+
+ assert(defined(invoker.java_files) || defined(invoker.DEPRECATED_java_in_dir)
+ || defined(invoker.srcjars))
+
+ _srcjar_deps = []
+ if (defined(invoker.srcjar_deps)) {
+ _srcjar_deps = invoker.srcjar_deps
+ }
+
+ _srcjars = []
+ if (defined(invoker.srcjars)) {
+ _srcjars = invoker.srcjars
+ }
+
+ _java_files = []
+ if (defined(invoker.java_files)) {
+ _java_files = invoker.java_files
+ } else if (defined(invoker.DEPRECATED_java_in_dir)) {
+ _src_dir = invoker.DEPRECATED_java_in_dir + "/src"
+ _src_dir_exists = exec_script("//build/dir_exists.py",
+ [ rebase_path(_src_dir, root_build_dir) ],
+ "string")
+ assert(_src_dir_exists == "False",
+ "In GN, java_in_dir should be the fully specified java directory " +
+ "(i.e. including the trailing \"/src\")")
+
+ _java_files_build_rel = exec_script(
+ "//build/android/gyp/find.py",
+ [
+ "--pattern",
+ "*.java",
+ rebase_path(invoker.DEPRECATED_java_in_dir, root_build_dir)
+ ],
+ "list lines"
+ )
+ _java_files = rebase_path(_java_files_build_rel, ".", root_build_dir)
+ }
+ assert(_java_files != [] || _srcjar_deps != [] || _srcjars != [])
+
+ # TODO(cjhopman): Write a proper build config so that java library
+ # dependencies work correctly.
+ _build_config = "$target_gen_dir/$target_name.build_config"
+ write_file(
+ _build_config,
+ "{ \"javac\": { \"classpath\": [], \"srcjars\": [] } }")
+
+ _jar_path = "$root_build_dir/lib.java/$target_name.jar"
+ if (defined(invoker.jar_path)) {
+ _jar_path = invoker.jar_path
+ }
+
+ compile_java(target_name) {
+ build_config = _build_config
+ jar_path = _jar_path
+ java_files = _java_files
+ srcjar_deps = _srcjar_deps
+ srcjars = _srcjars
+ }
+}
+
# Declare an Android library target for a prebuilt jar
#
@@ -875,28 +941,6 @@
_dist_jar_path = _dist_jar_path_list[0]
_native_libs = []
- if (defined(invoker.native_libs)) {
- _use_chromium_linker = false
- if (defined(invoker.use_chromium_linker)) {
- _use_chromium_linker = invoker.use_chromium_linker
- }
-
- # TODO(GYP) add "|| profiling_full_stack_frames
- # Only enable the chromium linker on regular builds, since the
- # component build crashes on Android 4.4. See b/11379966
- if (is_component_build || cpu_arch == "arm64" || cpu_arch == "x64") {
- _use_chromium_linker = false
- }
-
- _native_libs = invoker.native_libs
- _native_libs_dir = base_path + "/libs"
-
- if (_use_chromium_linker) {
- _native_libs += [
- "$root_build_dir/lib.stripped/libchromium_android_linker.so"
- ]
- }
- }
_keystore_path = android_default_keystore_path
_keystore_name = android_default_keystore_name
@@ -913,6 +957,40 @@
_srcjar_deps += invoker.srcjar_deps
}
+ _load_library_from_apk = false
+
+ if (defined(invoker.native_libs)) {
+ _use_chromium_linker = false
+ if (defined(invoker.use_chromium_linker)) {
+ _use_chromium_linker = (invoker.use_chromium_linker &&
+ chromium_linker_supported)
+ }
+
+ if (defined(invoker.load_library_from_apk) &&
+ invoker.load_library_from_apk) {
+ _load_library_from_apk = true
+ assert(_use_chromium_linker, "Loading library from the apk requires use" +
+ " of the Chromium linker.")
+ }
+
+ _enable_relocation_packing = false
+ if (defined(invoker.enable_relocation_packing) &&
+ invoker.enable_relocation_packing) {
+ _enable_relocation_packing = relocation_packing_supported
+ assert(_use_chromium_linker, "Relocation packing requires use of the" +
+ " Chromium linker.")
+ }
+
+ _native_libs = invoker.native_libs
+ _native_libs_dir = base_path + "/libs"
+
+ if (_use_chromium_linker) {
+ _native_libs += [
+ "$root_build_dir/lib.stripped/libchromium_android_linker.so"
+ ]
+ }
+ }
+
_rebased_build_config = rebase_path(build_config, root_build_dir)
write_build_config("${_template_name}__build_config") {
@@ -945,7 +1023,6 @@
_enable_chromium_linker_tests = invoker.enable_chromium_linker_tests
}
- _load_library_from_apk = false
_native_lib_version_name = ""
java_cpp_template("${_template_name}__native_libraries_java") {
@@ -1025,19 +1102,48 @@
}
if (_native_libs != []) {
- copy_ex("${_template_name}__prepare_native") {
- clear_dir = true
+ action("${_template_name}__prepare_native") {
+ script = "//build/android/gyp/pack_arm_relocations.py"
+ packed_libraries_dir = "$_native_libs_dir/$android_app_abi"
+ depfile = "$target_gen_dir/$target_name.d"
+ outputs = [
+ depfile
+ ]
inputs = [
build_config
]
- dest = "$_native_libs_dir/$android_app_abi"
- args = [
- "--files=@FileArg(${_rebased_build_config}:native:libraries)",
+ deps = []
+ skip_packing_list = [
+ "gdbserver",
+ "libchromium_android_linker.so",
]
+
+ enable_packing_arg = 0
+ if (_enable_relocation_packing) {
+ enable_packing_arg = 1
+ deps += [
+ relocation_packer_target
+ ]
+ }
+
+ args = [
+ "--depfile", rebase_path(depfile, root_build_dir),
+ "--enable-packing=$enable_packing_arg",
+ "--has-relocations-with-addends=$relocations_have_addends",
+ "--exclude-packing-list=$skip_packing_list",
+ "--android-pack-relocations", rebase_path(relocation_packer_exe, root_build_dir),
+ "--android-objcopy", rebase_path(android_objcopy, root_build_dir),
+ "--stripped-libraries-dir", rebase_path(root_build_dir, root_build_dir),
+ "--packed-libraries-dir", rebase_path(packed_libraries_dir, root_build_dir),
+ "--libraries=@FileArg(${_rebased_build_config}:native:libraries)",
+ "--clear-dir"
+ ]
+
if (is_debug) {
- rebased_gdbserver = rebase_path(android_gdbserver, root_build_dir)
+ rebased_gdbserver = rebase_path([ android_gdbserver ], root_build_dir)
+ inputs += [ android_gdbserver ]
args += [
- "--files=[\"$rebased_gdbserver\"]"
+ "--libraries=$rebased_gdbserver"
]
}
}
@@ -1049,6 +1155,7 @@
android_manifest = invoker.android_manifest
resources_zip = all_resources_zip_path
dex_path = final_dex_path
+ load_library_from_apk = _load_library_from_apk
if (defined(invoker.asset_location)) {
asset_location = invoker.asset_location
diff --git a/build/config/features.gni b/build/config/features.gni
index 4ed933f..49fe155 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -36,6 +36,9 @@
#enable_webrtc = !is_ios TODO(GYP) use this condition when WebRTC works in
# the GN build.
enable_webrtc = false
+
+ # Enables proprietary codecs and demuxers; e.g. H264, MOV, AAC, and MP3.
+ proprietary_codecs = false
}
# Additional dependent variables -----------------------------------------------
diff --git a/build/get_landmines.py b/build/get_landmines.py
index f15d8db..14170df 100755
--- a/build/get_landmines.py
+++ b/build/get_landmines.py
@@ -57,8 +57,7 @@
print 'blink_resources.grd changed: crbug.com/400860'
print 'ninja dependency cycle: crbug.com/408192'
if platform() == 'android':
- print 'Clobber: To delete stale generated .java files.'
- print 'Delete stale generated .java files again. crbug.com/349592'
+ print 'Delete stale generated .java files yet again. crbug.com/349592'
def main():
diff --git a/build/get_syzygy_binaries.py b/build/get_syzygy_binaries.py
index 79a186d..b5424c2 100755
--- a/build/get_syzygy_binaries.py
+++ b/build/get_syzygy_binaries.py
@@ -46,9 +46,7 @@
('benchmark.zip', 'benchmark', '', None),
('binaries.zip', 'binaries', 'exe', None),
('symbols.zip', 'symbols', 'exe',
- lambda x: x.filename.endswith('.dll.pdb')),
- ('include.zip', 'include', 'include', None),
- ('lib.zip', 'library', 'lib', None)]
+ lambda x: x.filename.endswith('.dll.pdb'))]
def _Shell(*cmd, **kw):
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index f00a8be..a0f11db 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -4,7 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Script to install everything needed to build mojo (well, ideally, anyway)
+# Script to install everything needed to build chromium (well, ideally, anyway)
# See http://code.google.com/p/chromium/wiki/LinuxBuildInstructions
# and http://code.google.com/p/chromium/wiki/LinuxBuild64Bit
@@ -248,7 +248,7 @@
if test "$do_inst_syms" = "" && test 0 -eq ${do_quick_check-0}
then
- echo "This script installs all tools and libraries needed to build Mojo."
+ echo "This script installs all tools and libraries needed to build Chromium."
echo ""
echo "For most of the libraries, it can also install debugging symbols, which"
echo "will allow you to debug code in the system libraries. Most developers"
diff --git a/build/sanitizers/sanitizer_options.cc b/build/sanitizers/sanitizer_options.cc
index 1e92625..821ba48 100644
--- a/build/sanitizers/sanitizer_options.cc
+++ b/build/sanitizers/sanitizer_options.cc
@@ -46,6 +46,9 @@
// strip_path_prefix=Release/../../ - prefixes up to and including this
// substring will be stripped from source file paths in symbolized reports
// (if symbolize=true, which is set when running with LeakSanitizer).
+// fast_unwind_on_fatal=1 - use the fast (frame-pointer-based) stack unwinder
+// to print error reports. V8 doesn't generate debug info for the JIT code,
+// so the slow unwinder may not work properly.
#if defined(OS_LINUX)
#if defined(GOOGLE_CHROME_BUILD)
// Default AddressSanitizer options for the official build. These do not affect
@@ -54,18 +57,18 @@
const char kAsanDefaultOptions[] =
"legacy_pthread_cond=1 malloc_context_size=5 strict_memcmp=0 "
"symbolize=false check_printf=1 use_sigaltstack=1 detect_leaks=0 "
- "strip_path_prefix=Release/../../ ";
+ "strip_path_prefix=Release/../../ fast_unwind_on_fatal=1";
#else
// Default AddressSanitizer options for buildbots and non-official builds.
const char *kAsanDefaultOptions =
"strict_memcmp=0 symbolize=false check_printf=1 use_sigaltstack=1 "
- "detect_leaks=0 strip_path_prefix=Release/../../ ";
+ "detect_leaks=0 strip_path_prefix=Release/../../ fast_unwind_on_fatal=1";
#endif // GOOGLE_CHROME_BUILD
#elif defined(OS_MACOSX)
const char *kAsanDefaultOptions =
"strict_memcmp=0 replace_intrin=0 check_printf=1 use_sigaltstack=1 "
- "strip_path_prefix=Release/../../ ";
+ "strip_path_prefix=Release/../../ fast_unwind_on_fatal=1";
static const char kNaClDefaultOptions[] = "handle_segv=0";
static const char kNaClFlag[] = "--type=nacl-loader";
#endif // OS_LINUX
diff --git a/cc/animation/keyframed_animation_curve.cc b/cc/animation/keyframed_animation_curve.cc
index de18a2b..333a5c9 100644
--- a/cc/animation/keyframed_animation_curve.cc
+++ b/cc/animation/keyframed_animation_curve.cc
@@ -6,7 +6,7 @@
#include "cc/animation/keyframed_animation_curve.h"
#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
namespace cc {
diff --git a/cc/animation/keyframed_animation_curve_unittest.cc b/cc/animation/keyframed_animation_curve_unittest.cc
index 32bc141..71fd521 100644
--- a/cc/animation/keyframed_animation_curve_unittest.cc
+++ b/cc/animation/keyframed_animation_curve_unittest.cc
@@ -8,7 +8,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
#include "ui/gfx/test/gfx_util.h"
namespace cc {
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index ab6db10..b779a37 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -16,7 +16,7 @@
#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/base/scoped_ptr_algorithm.h"
#include "cc/output/filter_operations.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc
index 96c406c..49ac0a5 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -14,7 +14,7 @@
#include "cc/test/animation_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/animation/scrollbar_animation_controller.h b/cc/animation/scrollbar_animation_controller.h
index 8c98c9e..92efe75 100644
--- a/cc/animation/scrollbar_animation_controller.h
+++ b/cc/animation/scrollbar_animation_controller.h
@@ -9,7 +9,7 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "cc/base/cc_export.h"
-#include "ui/gfx/vector2d_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
diff --git a/cc/animation/transform_operation.cc b/cc/animation/transform_operation.cc
index f4403fa..e9ae86d 100644
--- a/cc/animation/transform_operation.cc
+++ b/cc/animation/transform_operation.cc
@@ -14,9 +14,9 @@
#include "base/logging.h"
#include "cc/animation/transform_operation.h"
#include "cc/animation/transform_operations.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/transform_util.h"
-#include "ui/gfx/vector3d_f.h"
namespace {
const SkMScalar kAngleEpsilon = 1e-4f;
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index 7c328af..7bc4ce2 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -7,9 +7,9 @@
#include <algorithm>
#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/transform_util.h"
-#include "ui/gfx/vector3d_f.h"
namespace cc {
diff --git a/cc/animation/transform_operations_unittest.cc b/cc/animation/transform_operations_unittest.cc
index cf29e97..7aae696 100644
--- a/cc/animation/transform_operations_unittest.cc
+++ b/cc/animation/transform_operations_unittest.cc
@@ -10,9 +10,9 @@
#include "cc/test/geometry_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/box_f.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/vector3d_f.h"
+#include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/vector3d_f.h"
namespace cc {
namespace {
diff --git a/cc/base/float_quad_unittest.cc b/cc/base/float_quad_unittest.cc
index c2186fd..6d9ce02 100644
--- a/cc/base/float_quad_unittest.cc
+++ b/cc/base/float_quad_unittest.cc
@@ -4,8 +4,8 @@
#include "cc/base/math_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/base/invalidation_region.h b/cc/base/invalidation_region.h
index 9cb2fe3..7aa4a8b 100644
--- a/cc/base/invalidation_region.h
+++ b/cc/base/invalidation_region.h
@@ -7,7 +7,7 @@
#include "cc/base/cc_export.h"
#include "cc/base/region.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
index 8cd0692..816f6fc 100644
--- a/cc/base/math_util.cc
+++ b/cc/base/math_util.cc
@@ -10,12 +10,12 @@
#include "base/debug/trace_event_argument.h"
#include "base/values.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/transform.h"
-#include "ui/gfx/vector2d_f.h"
namespace cc {
diff --git a/cc/base/math_util.h b/cc/base/math_util.h
index 622ea4d..50494e1 100644
--- a/cc/base/math_util.h
+++ b/cc/base/math_util.h
@@ -12,11 +12,11 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/scroll_offset.h"
-#include "ui/gfx/point3_f.h"
-#include "ui/gfx/point_f.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
#include "ui/gfx/transform.h"
namespace base {
diff --git a/cc/base/math_util_unittest.cc b/cc/base/math_util_unittest.cc
index fb8c27f..325870d 100644
--- a/cc/base/math_util_unittest.cc
+++ b/cc/base/math_util_unittest.cc
@@ -9,8 +9,8 @@
#include "cc/test/geometry_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/base/region.h b/cc/base/region.h
index d78d4cb..294ea63 100644
--- a/cc/base/region.h
+++ b/cc/base/region.h
@@ -10,7 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/skia_util.h"
namespace base {
diff --git a/cc/base/simple_enclosed_region.h b/cc/base/simple_enclosed_region.h
index c9625ba..5e8a6b2 100644
--- a/cc/base/simple_enclosed_region.h
+++ b/cc/base/simple_enclosed_region.h
@@ -8,7 +8,7 @@
#include <string>
#include "cc/base/cc_export.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/base/tiling_data.cc b/cc/base/tiling_data.cc
index 61bb851..7b39878 100644
--- a/cc/base/tiling_data.cc
+++ b/cc/base/tiling_data.cc
@@ -6,8 +6,8 @@
#include <algorithm>
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
namespace cc {
diff --git a/cc/base/tiling_data.h b/cc/base/tiling_data.h
index 08d421c..5e8a4d6 100644
--- a/cc/base/tiling_data.h
+++ b/cc/base/tiling_data.h
@@ -10,8 +10,8 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "cc/base/cc_export.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
namespace gfx {
class Vector2d;
diff --git a/cc/blink/web_layer_impl_fixed_bounds.h b/cc/blink/web_layer_impl_fixed_bounds.h
index 907975a..099feaf 100644
--- a/cc/blink/web_layer_impl_fixed_bounds.h
+++ b/cc/blink/web_layer_impl_fixed_bounds.h
@@ -6,7 +6,7 @@
#define CC_BLINK_WEB_LAYER_IMPL_FIXED_BOUNDS_H_
#include "cc/blink/web_layer_impl.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
#include "ui/gfx/transform.h"
namespace cc_blink {
diff --git a/cc/blink/web_layer_impl_fixed_bounds_unittest.cc b/cc/blink/web_layer_impl_fixed_bounds_unittest.cc
index f439366..ceeb311 100644
--- a/cc/blink/web_layer_impl_fixed_bounds_unittest.cc
+++ b/cc/blink/web_layer_impl_fixed_bounds_unittest.cc
@@ -12,7 +12,7 @@
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebSize.h"
#include "third_party/skia/include/utils/SkMatrix44.h"
-#include "ui/gfx/point3_f.h"
+#include "ui/gfx/geometry/point3_f.h"
using blink::WebFloatPoint;
using blink::WebSize;
diff --git a/cc/debug/debug_rect_history.h b/cc/debug/debug_rect_history.h
index 278b208..6d25b29 100644
--- a/cc/debug/debug_rect_history.h
+++ b/cc/debug/debug_rect_history.h
@@ -9,7 +9,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "cc/layers/layer_lists.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/debug/invalidation_benchmark.cc b/cc/debug/invalidation_benchmark.cc
index 87d2edd..1a6a2b8 100644
--- a/cc/debug/invalidation_benchmark.cc
+++ b/cc/debug/invalidation_benchmark.cc
@@ -14,7 +14,7 @@
#include "cc/layers/picture_layer.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_host_common.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/debug/picture_record_benchmark.cc b/cc/debug/picture_record_benchmark.cc
index a8a296d..5f832ae 100644
--- a/cc/debug/picture_record_benchmark.cc
+++ b/cc/debug/picture_record_benchmark.cc
@@ -12,7 +12,7 @@
#include "cc/layers/picture_layer.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_host_common.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/debug/rasterize_and_record_benchmark.cc b/cc/debug/rasterize_and_record_benchmark.cc
index 7a1319d..f24dce3 100644
--- a/cc/debug/rasterize_and_record_benchmark.cc
+++ b/cc/debug/rasterize_and_record_benchmark.cc
@@ -17,7 +17,7 @@
#include "cc/layers/picture_layer.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_host_common.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/debug/rasterize_and_record_benchmark_impl.cc b/cc/debug/rasterize_and_record_benchmark_impl.cc
index 57e7f15..612dad8 100644
--- a/cc/debug/rasterize_and_record_benchmark_impl.cc
+++ b/cc/debug/rasterize_and_record_benchmark_impl.cc
@@ -15,7 +15,7 @@
#include "cc/resources/raster_worker_pool.h"
#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_host_impl.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/input/layer_scroll_offset_delegate.h b/cc/input/layer_scroll_offset_delegate.h
index f7ca485..02ba011 100644
--- a/cc/input/layer_scroll_offset_delegate.h
+++ b/cc/input/layer_scroll_offset_delegate.h
@@ -7,7 +7,7 @@
#include "base/basictypes.h"
#include "ui/gfx/geometry/scroll_offset.h"
-#include "ui/gfx/size_f.h"
+#include "ui/gfx/geometry/size_f.h"
namespace cc {
diff --git a/cc/input/page_scale_animation.cc b/cc/input/page_scale_animation.cc
index 23bf21b..111502b 100644
--- a/cc/input/page_scale_animation.cc
+++ b/cc/input/page_scale_animation.cc
@@ -8,9 +8,9 @@
#include "base/logging.h"
#include "cc/animation/timing_function.h"
-#include "ui/gfx/point_f.h"
-#include "ui/gfx/rect_f.h"
-#include "ui/gfx/vector2d_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
namespace {
diff --git a/cc/input/page_scale_animation.h b/cc/input/page_scale_animation.h
index fca9563..ce13dcf 100644
--- a/cc/input/page_scale_animation.h
+++ b/cc/input/page_scale_animation.h
@@ -9,8 +9,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "cc/base/cc_export.h"
-#include "ui/gfx/size.h"
-#include "ui/gfx/vector2d_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
diff --git a/cc/input/scrollbar.h b/cc/input/scrollbar.h
index 8321ae2..19ac92a 100644
--- a/cc/input/scrollbar.h
+++ b/cc/input/scrollbar.h
@@ -6,8 +6,8 @@
#define CC_INPUT_SCROLLBAR_H_
#include "cc/base/cc_export.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
class SkCanvas;
diff --git a/cc/input/top_controls_manager.cc b/cc/input/top_controls_manager.cc
index 18a93a5..cd3534c 100644
--- a/cc/input/top_controls_manager.cc
+++ b/cc/input/top_controls_manager.cc
@@ -13,8 +13,8 @@
#include "cc/output/begin_frame_args.h"
#include "cc/trees/layer_tree_impl.h"
#include "ui/gfx/frame_time.h"
+#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/transform.h"
-#include "ui/gfx/vector2d_f.h"
namespace cc {
namespace {
diff --git a/cc/input/top_controls_manager.h b/cc/input/top_controls_manager.h
index 1aface8..a90d33d 100644
--- a/cc/input/top_controls_manager.h
+++ b/cc/input/top_controls_manager.h
@@ -9,8 +9,8 @@
#include "base/memory/weak_ptr.h"
#include "cc/input/top_controls_state.h"
#include "cc/layers/layer_impl.h"
-#include "ui/gfx/size.h"
-#include "ui/gfx/vector2d_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector2d_f.h"
namespace base {
class TimeTicks;
diff --git a/cc/input/top_controls_manager_unittest.cc b/cc/input/top_controls_manager_unittest.cc
index 9bde132..77b40c9 100644
--- a/cc/input/top_controls_manager_unittest.cc
+++ b/cc/input/top_controls_manager_unittest.cc
@@ -14,7 +14,7 @@
#include "cc/trees/layer_tree_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/frame_time.h"
-#include "ui/gfx/vector2d_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
namespace {
diff --git a/cc/layers/contents_scaling_layer.cc b/cc/layers/contents_scaling_layer.cc
index 5811bdc..733bac0 100644
--- a/cc/layers/contents_scaling_layer.cc
+++ b/cc/layers/contents_scaling_layer.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "cc/layers/contents_scaling_layer.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
diff --git a/cc/layers/delegated_frame_provider.h b/cc/layers/delegated_frame_provider.h
index b71c748..3bcf919 100644
--- a/cc/layers/delegated_frame_provider.h
+++ b/cc/layers/delegated_frame_provider.h
@@ -12,8 +12,8 @@
#include "cc/base/cc_export.h"
#include "cc/resources/return_callback.h"
#include "cc/resources/returned_resource.h"
-#include "ui/gfx/rect_f.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
class DelegatedFrameData;
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h
index 23845a0..403393e 100644
--- a/cc/layers/draw_properties.h
+++ b/cc/layers/draw_properties.h
@@ -6,7 +6,7 @@
#define CC_LAYERS_DRAW_PROPERTIES_H_
#include "base/memory/scoped_ptr.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 1dbdc64..0692139 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -25,8 +25,8 @@
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/layers/io_surface_layer_impl.h b/cc/layers/io_surface_layer_impl.h
index 17b7093..663bcef 100644
--- a/cc/layers/io_surface_layer_impl.h
+++ b/cc/layers/io_surface_layer_impl.h
@@ -9,7 +9,7 @@
#include "cc/base/cc_export.h"
#include "cc/layers/layer_impl.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index dd71307..08f39b9 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -26,8 +26,8 @@
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "third_party/skia/include/core/SkImageFilter.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
-#include "ui/gfx/rect_conversions.h"
namespace cc {
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index c6279af..f372186 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -29,10 +29,10 @@
#include "third_party/skia/include/core/SkImageFilter.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/scroll_offset.h"
-#include "ui/gfx/point3_f.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
#include "ui/gfx/transform.h"
namespace gfx {
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 2973519..391fa92 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -26,12 +26,12 @@
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/proxy.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
-#include "ui/gfx/point_conversions.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
namespace cc {
LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id)
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index bcac36c..1c14eeb 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -31,10 +31,10 @@
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkImageFilter.h"
#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/scroll_offset.h"
-#include "ui/gfx/point3_f.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
#include "ui/gfx/transform.h"
namespace base {
diff --git a/cc/layers/layer_utils.cc b/cc/layers/layer_utils.cc
index 9c299c2..888174a 100644
--- a/cc/layers/layer_utils.cc
+++ b/cc/layers/layer_utils.cc
@@ -6,7 +6,7 @@
#include "cc/layers/layer_impl.h"
#include "cc/trees/layer_tree_host_common.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
namespace cc {
diff --git a/cc/layers/layer_utils_unittest.cc b/cc/layers/layer_utils_unittest.cc
index bf4e9db..1eb1986 100644
--- a/cc/layers/layer_utils_unittest.cc
+++ b/cc/layers/layer_utils_unittest.cc
@@ -11,7 +11,7 @@
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/box_f.h"
#include "ui/gfx/test/gfx_util.h"
namespace cc {
diff --git a/cc/layers/nine_patch_layer.h b/cc/layers/nine_patch_layer.h
index f5aa712..9054610 100644
--- a/cc/layers/nine_patch_layer.h
+++ b/cc/layers/nine_patch_layer.h
@@ -10,7 +10,7 @@
#include "cc/layers/layer.h"
#include "cc/layers/ui_resource_layer.h"
#include "cc/resources/ui_resource_client.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/layers/nine_patch_layer_impl.cc b/cc/layers/nine_patch_layer_impl.cc
index b350ba9..1c76d11 100644
--- a/cc/layers/nine_patch_layer_impl.cc
+++ b/cc/layers/nine_patch_layer_impl.cc
@@ -10,7 +10,7 @@
#include "cc/quads/texture_draw_quad.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/occlusion.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect_f.h"
namespace cc {
diff --git a/cc/layers/nine_patch_layer_impl.h b/cc/layers/nine_patch_layer_impl.h
index 9af6f32..5320d39 100644
--- a/cc/layers/nine_patch_layer_impl.h
+++ b/cc/layers/nine_patch_layer_impl.h
@@ -12,8 +12,8 @@
#include "cc/layers/ui_resource_layer_impl.h"
#include "cc/resources/resource_provider.h"
#include "cc/resources/ui_resource_client.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
namespace base {
class DictionaryValue;
diff --git a/cc/layers/nine_patch_layer_impl_unittest.cc b/cc/layers/nine_patch_layer_impl_unittest.cc
index 2b82d48..730b3e5 100644
--- a/cc/layers/nine_patch_layer_impl_unittest.cc
+++ b/cc/layers/nine_patch_layer_impl_unittest.cc
@@ -15,8 +15,8 @@
#include "cc/trees/single_thread_proxy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/safe_integer_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/layers/paint_properties.h b/cc/layers/paint_properties.h
index 323e949..b57f90f 100644
--- a/cc/layers/paint_properties.h
+++ b/cc/layers/paint_properties.h
@@ -5,7 +5,7 @@
#ifndef CC_LAYERS_PAINT_PROPERTIES_H_
#define CC_LAYERS_PAINT_PROPERTIES_H_
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/layers/painted_scrollbar_layer_impl.cc b/cc/layers/painted_scrollbar_layer_impl.cc
index dcb1e82..ea48086 100644
--- a/cc/layers/painted_scrollbar_layer_impl.cc
+++ b/cc/layers/painted_scrollbar_layer_impl.cc
@@ -13,7 +13,7 @@
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/occlusion.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
diff --git a/cc/layers/picture_image_layer.h b/cc/layers/picture_image_layer.h
index 3467804..3575d88 100644
--- a/cc/layers/picture_image_layer.h
+++ b/cc/layers/picture_image_layer.h
@@ -9,7 +9,7 @@
#include "cc/layers/content_layer_client.h"
#include "cc/layers/picture_layer.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 1bb7d54..ca4033f 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -9,7 +9,7 @@
#include "cc/layers/picture_layer_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 2599852..687243c 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -26,9 +26,9 @@
#include "cc/resources/tile_manager.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/occlusion.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace {
const float kMaxScaleRatioDuringPinch = 2.0f;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 038dc2a..e8c83e1 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -28,8 +28,8 @@
#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/layers/render_surface.h b/cc/layers/render_surface.h
index 4da8d23..1e3719d 100644
--- a/cc/layers/render_surface.h
+++ b/cc/layers/render_surface.h
@@ -12,8 +12,8 @@
#include "base/memory/ref_counted.h"
#include "cc/base/cc_export.h"
#include "cc/layers/layer_lists.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index ecb97a3..9d36235 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -20,7 +20,7 @@
#include "cc/trees/damage_tracker.h"
#include "cc/trees/occlusion_tracker.h"
#include "third_party/skia/include/core/SkImageFilter.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 52e4f38..3303e94 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -14,8 +14,8 @@
#include "cc/layers/layer_lists.h"
#include "cc/quads/render_pass.h"
#include "cc/quads/shared_quad_state.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/layers/scrollbar_layer_impl_base.cc b/cc/layers/scrollbar_layer_impl_base.cc
index 8d93a54..4248617 100644
--- a/cc/layers/scrollbar_layer_impl_base.cc
+++ b/cc/layers/scrollbar_layer_impl_base.cc
@@ -6,7 +6,7 @@
#include <algorithm>
#include "cc/trees/layer_tree_impl.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
diff --git a/cc/layers/tiled_layer.cc b/cc/layers/tiled_layer.cc
index e4d5f7c..2a32741 100644
--- a/cc/layers/tiled_layer.cc
+++ b/cc/layers/tiled_layer.cc
@@ -19,7 +19,7 @@
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/occlusion_tracker.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
diff --git a/cc/layers/tiled_layer_impl.cc b/cc/layers/tiled_layer_impl.cc
index 6052591..c66e124 100644
--- a/cc/layers/tiled_layer_impl.cc
+++ b/cc/layers/tiled_layer_impl.cc
@@ -19,7 +19,7 @@
#include "cc/trees/occlusion.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/quad_f.h"
+#include "ui/gfx/geometry/quad_f.h"
namespace cc {
diff --git a/cc/layers/tiled_layer_unittest.cc b/cc/layers/tiled_layer_unittest.cc
index 862ff1f..0f3112c 100644
--- a/cc/layers/tiled_layer_unittest.cc
+++ b/cc/layers/tiled_layer_unittest.cc
@@ -25,7 +25,7 @@
#include "cc/trees/occlusion_tracker.h"
#include "cc/trees/single_thread_proxy.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/layers/ui_resource_layer.h b/cc/layers/ui_resource_layer.h
index e22318c..3bfd9b2 100644
--- a/cc/layers/ui_resource_layer.h
+++ b/cc/layers/ui_resource_layer.h
@@ -9,7 +9,7 @@
#include "cc/base/cc_export.h"
#include "cc/layers/layer.h"
#include "cc/resources/ui_resource_client.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/layers/ui_resource_layer_impl.cc b/cc/layers/ui_resource_layer_impl.cc
index dc08a90..35d242b 100644
--- a/cc/layers/ui_resource_layer_impl.cc
+++ b/cc/layers/ui_resource_layer_impl.cc
@@ -10,7 +10,7 @@
#include "cc/quads/texture_draw_quad.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/occlusion.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect_f.h"
namespace cc {
diff --git a/cc/layers/ui_resource_layer_impl.h b/cc/layers/ui_resource_layer_impl.h
index 515a0f1..d98f1fb 100644
--- a/cc/layers/ui_resource_layer_impl.h
+++ b/cc/layers/ui_resource_layer_impl.h
@@ -11,8 +11,8 @@
#include "cc/layers/layer_impl.h"
#include "cc/resources/resource_provider.h"
#include "cc/resources/ui_resource_client.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
namespace base {
class DictionaryValue;
diff --git a/cc/output/copy_output_request.h b/cc/output/copy_output_request.h
index 60a5f11..58aa790 100644
--- a/cc/output/copy_output_request.h
+++ b/cc/output/copy_output_request.h
@@ -10,7 +10,7 @@
#include "cc/base/cc_export.h"
#include "cc/resources/single_release_callback.h"
#include "cc/resources/texture_mailbox.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
class SkBitmap;
diff --git a/cc/output/copy_output_result.h b/cc/output/copy_output_result.h
index 8529e3f..3662818 100644
--- a/cc/output/copy_output_result.h
+++ b/cc/output/copy_output_result.h
@@ -9,7 +9,7 @@
#include "cc/base/cc_export.h"
#include "cc/resources/single_release_callback.h"
#include "cc/resources/texture_mailbox.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
class SkBitmap;
diff --git a/cc/output/filter_operations_unittest.cc b/cc/output/filter_operations_unittest.cc
index e97379c..d6ae6ab 100644
--- a/cc/output/filter_operations_unittest.cc
+++ b/cc/output/filter_operations_unittest.cc
@@ -6,7 +6,7 @@
#include "skia/ext/refptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkBlurImageFilter.h"
-#include "ui/gfx/point.h"
+#include "ui/gfx/geometry/point.h"
namespace cc {
namespace {
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 24e9f30..0dd6ec7 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -374,6 +374,9 @@
use_sync_query_ = context_caps.gpu.sync_query;
use_blend_minmax_ = context_caps.gpu.blend_minmax;
+ use_blend_equation_advanced_ = context_caps.gpu.blend_equation_advanced;
+ use_blend_equation_advanced_coherent_ =
+ context_caps.gpu.blend_equation_advanced_coherent;
InitializeSharedObjects();
}
@@ -734,7 +737,8 @@
}
bool GLRenderer::CanApplyBlendModeUsingBlendFunc(SkXfermode::Mode blend_mode) {
- return (use_blend_minmax_ && blend_mode == SkXfermode::kLighten_Mode) ||
+ return use_blend_equation_advanced_ ||
+ (use_blend_minmax_ && blend_mode == SkXfermode::kLighten_Mode) ||
blend_mode == SkXfermode::kScreen_Mode ||
blend_mode == SkXfermode::kSrcOver_Mode;
}
@@ -743,11 +747,67 @@
DCHECK(CanApplyBlendModeUsingBlendFunc(blend_mode));
// Any modes set here must be reset in RestoreBlendFuncToDefault
- if (blend_mode == SkXfermode::kScreen_Mode) {
- GLC(gl_, gl_->BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE));
- } else if (blend_mode == SkXfermode::kLighten_Mode) {
- GLC(gl_, gl_->BlendFunc(GL_ONE, GL_ONE));
- GLC(gl_, gl_->BlendEquation(GL_MAX_EXT));
+ if (use_blend_equation_advanced_) {
+ GLenum equation = GL_FUNC_ADD;
+
+ switch (blend_mode) {
+ case SkXfermode::kScreen_Mode:
+ equation = GL_SCREEN_KHR;
+ break;
+ case SkXfermode::kOverlay_Mode:
+ equation = GL_OVERLAY_KHR;
+ break;
+ case SkXfermode::kDarken_Mode:
+ equation = GL_DARKEN_KHR;
+ break;
+ case SkXfermode::kLighten_Mode:
+ equation = GL_LIGHTEN_KHR;
+ break;
+ case SkXfermode::kColorDodge_Mode:
+ equation = GL_COLORDODGE_KHR;
+ break;
+ case SkXfermode::kColorBurn_Mode:
+ equation = GL_COLORBURN_KHR;
+ break;
+ case SkXfermode::kHardLight_Mode:
+ equation = GL_HARDLIGHT_KHR;
+ break;
+ case SkXfermode::kSoftLight_Mode:
+ equation = GL_SOFTLIGHT_KHR;
+ break;
+ case SkXfermode::kDifference_Mode:
+ equation = GL_DIFFERENCE_KHR;
+ break;
+ case SkXfermode::kExclusion_Mode:
+ equation = GL_EXCLUSION_KHR;
+ break;
+ case SkXfermode::kMultiply_Mode:
+ equation = GL_MULTIPLY_KHR;
+ break;
+ case SkXfermode::kHue_Mode:
+ equation = GL_HSL_HUE_KHR;
+ break;
+ case SkXfermode::kSaturation_Mode:
+ equation = GL_HSL_SATURATION_KHR;
+ break;
+ case SkXfermode::kColor_Mode:
+ equation = GL_HSL_COLOR_KHR;
+ break;
+ case SkXfermode::kLuminosity_Mode:
+ equation = GL_HSL_LUMINOSITY_KHR;
+ break;
+ default:
+ return;
+ }
+
+ GLC(gl_, gl_->BlendEquation(equation));
+ } else {
+ if (blend_mode == SkXfermode::kScreen_Mode) {
+ GLC(gl_, gl_->BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE));
+ } else if (blend_mode == SkXfermode::kLighten_Mode) {
+ GLC(gl_, gl_->BlendFunc(GL_ONE, GL_ONE));
+ GLC(gl_, gl_->BlendEquation(GL_MAX_EXT));
+ }
}
}
@@ -755,10 +815,14 @@
if (blend_mode == SkXfermode::kSrcOver_Mode)
return;
- GLC(gl_, gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
-
- if (blend_mode == SkXfermode::kLighten_Mode)
+ if (use_blend_equation_advanced_) {
GLC(gl_, gl_->BlendEquation(GL_FUNC_ADD));
+ } else {
+ GLC(gl_, gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
+
+ if (blend_mode == SkXfermode::kLighten_Mode)
+ GLC(gl_, gl_->BlendEquation(GL_FUNC_ADD));
+ }
}
bool GLRenderer::ShouldApplyBackgroundFilters(DrawingFrame* frame,
@@ -1094,8 +1158,12 @@
contents_resource_lock->target());
}
- if (CanApplyBlendModeUsingBlendFunc(blend_mode))
+ if (CanApplyBlendModeUsingBlendFunc(blend_mode)) {
+ if (!use_blend_equation_advanced_coherent_ && use_blend_equation_advanced_)
+ GLC(gl_, gl_->BlendBarrierKHR());
+
ApplyBlendModeUsingBlendFunc(blend_mode);
+ }
TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
gl_,
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 676e7b7..625d458 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -449,6 +449,8 @@
scoped_ptr<SyncQuery> current_sync_query_;
bool use_sync_query_;
bool use_blend_minmax_;
+ bool use_blend_equation_advanced_;
+ bool use_blend_equation_advanced_coherent_;
SkBitmap on_demand_tile_raster_bitmap_;
ResourceProvider::ResourceId on_demand_tile_raster_resource_id_;
diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h
index bb37a91..99b174b 100644
--- a/cc/output/output_surface_client.h
+++ b/cc/output/output_surface_client.h
@@ -11,7 +11,7 @@
#include "cc/base/cc_export.h"
#include "cc/output/begin_frame_args.h"
#include "cc/output/context_provider.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace gfx {
class Transform;
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index 98bde8d..b2d76a7 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -709,7 +709,7 @@
return Blend(src, dst);
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
// clang-format on
return "precision mediump float;" + GetHelperFunctions() +
@@ -738,7 +738,7 @@
return result;
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
static const std::string kFunctionColorDodgeComponent = SHADER0(
// clang-format on
@@ -753,7 +753,7 @@
return d * srca + srcc * (1.0 - dsta) + dstc * (1.0 - srca);
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
static const std::string kFunctionColorBurnComponent = SHADER0(
// clang-format on
@@ -767,7 +767,7 @@
return srca * d + srcc * (1.0 - dsta) + dstc * (1.0 - srca);
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
static const std::string kFunctionSoftLightComponentPosDstAlpha = SHADER0(
// clang-format on
@@ -792,7 +792,7 @@
}
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
static const std::string kFunctionLum = SHADER0(
// clang-format on
@@ -818,7 +818,7 @@
return outColor;
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
static const std::string kFunctionSat = SHADER0(
// clang-format on
@@ -866,7 +866,7 @@
return hueLumColor;
}
// clang-format off
- );
+ ); // NOLINT(whitespace/parens)
// clang-format on
switch (blend_mode_) {
diff --git a/cc/quads/content_draw_quad_base.h b/cc/quads/content_draw_quad_base.h
index 6917e6b..b0e53dc 100644
--- a/cc/quads/content_draw_quad_base.h
+++ b/cc/quads/content_draw_quad_base.h
@@ -9,8 +9,8 @@
#include "cc/base/cc_export.h"
#include "cc/quads/draw_quad.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/quads/draw_polygon.h b/cc/quads/draw_polygon.h
index 8e65ea8..32643ee 100644
--- a/cc/quads/draw_polygon.h
+++ b/cc/quads/draw_polygon.h
@@ -9,11 +9,11 @@
#include "cc/base/math_util.h"
#include "cc/output/bsp_compare_result.h"
-#include "ui/gfx/point3_f.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/transform.h"
-#include "ui/gfx/vector3d_f.h"
namespace cc {
diff --git a/cc/quads/draw_quad.cc b/cc/quads/draw_quad.cc
index 38b24b0..7322a7a 100644
--- a/cc/quads/draw_quad.cc
+++ b/cc/quads/draw_quad.cc
@@ -20,7 +20,7 @@
#include "cc/quads/texture_draw_quad.h"
#include "cc/quads/tile_draw_quad.h"
#include "cc/quads/yuv_video_draw_quad.h"
-#include "ui/gfx/quad_f.h"
+#include "ui/gfx/geometry/quad_f.h"
namespace cc {
diff --git a/cc/quads/io_surface_draw_quad.h b/cc/quads/io_surface_draw_quad.h
index 9c8b50b..ba64384 100644
--- a/cc/quads/io_surface_draw_quad.h
+++ b/cc/quads/io_surface_draw_quad.h
@@ -8,7 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/quads/draw_quad.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/quads/picture_draw_quad.h b/cc/quads/picture_draw_quad.h
index d2a7271..1bea1a2 100644
--- a/cc/quads/picture_draw_quad.h
+++ b/cc/quads/picture_draw_quad.h
@@ -10,9 +10,9 @@
#include "cc/base/cc_export.h"
#include "cc/quads/content_draw_quad_base.h"
#include "cc/resources/picture_pile_impl.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/quads/render_pass.h b/cc/quads/render_pass.h
index 65c712d..76536b5 100644
--- a/cc/quads/render_pass.h
+++ b/cc/quads/render_pass.h
@@ -15,8 +15,8 @@
#include "cc/quads/list_container.h"
#include "cc/quads/render_pass_id.h"
#include "skia/ext/refptr.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/transform.h"
namespace base {
diff --git a/cc/quads/shared_quad_state.h b/cc/quads/shared_quad_state.h
index dde87a8..ebf7d11 100644
--- a/cc/quads/shared_quad_state.h
+++ b/cc/quads/shared_quad_state.h
@@ -8,7 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "third_party/skia/include/core/SkXfermode.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/transform.h"
namespace base {
diff --git a/cc/quads/texture_draw_quad.cc b/cc/quads/texture_draw_quad.cc
index 72435c2..9ff9744 100644
--- a/cc/quads/texture_draw_quad.cc
+++ b/cc/quads/texture_draw_quad.cc
@@ -8,7 +8,7 @@
#include "base/logging.h"
#include "base/values.h"
#include "cc/base/math_util.h"
-#include "ui/gfx/vector2d_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
diff --git a/cc/quads/texture_draw_quad.h b/cc/quads/texture_draw_quad.h
index 3e0632c..9137d51 100644
--- a/cc/quads/texture_draw_quad.h
+++ b/cc/quads/texture_draw_quad.h
@@ -8,7 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/quads/draw_quad.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect_f.h"
namespace cc {
diff --git a/cc/resources/content_layer_updater.cc b/cc/resources/content_layer_updater.cc
index f6469ff..971362f 100644
--- a/cc/resources/content_layer_updater.cc
+++ b/cc/resources/content_layer_updater.cc
@@ -5,15 +5,13 @@
#include "cc/resources/content_layer_updater.h"
#include "base/debug/trace_event.h"
-#include "base/time/time.h"
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/resources/layer_painter.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkScalar.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/skia_util.h"
namespace cc {
diff --git a/cc/resources/content_layer_updater.h b/cc/resources/content_layer_updater.h
index e0c54e5..4ba18ba 100644
--- a/cc/resources/content_layer_updater.h
+++ b/cc/resources/content_layer_updater.h
@@ -7,7 +7,7 @@
#include "cc/base/cc_export.h"
#include "cc/resources/layer_updater.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
class SkCanvas;
diff --git a/cc/resources/gpu_memory_buffer_manager.h b/cc/resources/gpu_memory_buffer_manager.h
index 1e114c3..52434e0 100644
--- a/cc/resources/gpu_memory_buffer_manager.h
+++ b/cc/resources/gpu_memory_buffer_manager.h
@@ -7,8 +7,8 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
+#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
-#include "ui/gfx/size.h"
namespace cc {
diff --git a/cc/resources/layer_quad.cc b/cc/resources/layer_quad.cc
index fdd6866..38ec7b3 100644
--- a/cc/resources/layer_quad.cc
+++ b/cc/resources/layer_quad.cc
@@ -5,7 +5,7 @@
#include "cc/resources/layer_quad.h"
#include "base/logging.h"
-#include "ui/gfx/quad_f.h"
+#include "ui/gfx/geometry/quad_f.h"
namespace cc {
diff --git a/cc/resources/layer_quad.h b/cc/resources/layer_quad.h
index 1d71193..ed1db10 100644
--- a/cc/resources/layer_quad.h
+++ b/cc/resources/layer_quad.h
@@ -8,7 +8,7 @@
#include "base/basictypes.h"
#include "cc/base/cc_export.h"
-#include "ui/gfx/point_f.h"
+#include "ui/gfx/geometry/point_f.h"
namespace gfx {
class QuadF;
diff --git a/cc/resources/layer_quad_unittest.cc b/cc/resources/layer_quad_unittest.cc
index 8d3909c..3560b8d 100644
--- a/cc/resources/layer_quad_unittest.cc
+++ b/cc/resources/layer_quad_unittest.cc
@@ -5,7 +5,7 @@
#include "cc/resources/layer_quad.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/quad_f.h"
+#include "ui/gfx/geometry/quad_f.h"
namespace cc {
namespace {
diff --git a/cc/resources/layer_tiling_data.h b/cc/resources/layer_tiling_data.h
index b369d61..5168118 100644
--- a/cc/resources/layer_tiling_data.h
+++ b/cc/resources/layer_tiling_data.h
@@ -14,7 +14,7 @@
#include "cc/base/cc_export.h"
#include "cc/base/simple_enclosed_region.h"
#include "cc/base/tiling_data.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/resources/layer_updater.h b/cc/resources/layer_updater.h
index 4614c5a..a5eb0a1 100644
--- a/cc/resources/layer_updater.h
+++ b/cc/resources/layer_updater.h
@@ -9,8 +9,8 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
namespace cc {
diff --git a/cc/resources/one_copy_raster_worker_pool.cc b/cc/resources/one_copy_raster_worker_pool.cc
index 66b6ea0..cc5dd19 100644
--- a/cc/resources/one_copy_raster_worker_pool.cc
+++ b/cc/resources/one_copy_raster_worker_pool.cc
@@ -5,6 +5,7 @@
#include "cc/resources/one_copy_raster_worker_pool.h"
#include <algorithm>
+#include <limits>
#include "base/debug/trace_event.h"
#include "base/debug/trace_event_argument.h"
@@ -21,23 +22,28 @@
class RasterBufferImpl : public RasterBuffer {
public:
- RasterBufferImpl(ResourceProvider* resource_provider,
+ RasterBufferImpl(OneCopyRasterWorkerPool* worker_pool,
+ ResourceProvider* resource_provider,
ResourcePool* resource_pool,
const Resource* resource)
- : resource_provider_(resource_provider),
+ : worker_pool_(worker_pool),
+ resource_provider_(resource_provider),
resource_pool_(resource_pool),
resource_(resource),
raster_resource_(resource_pool->AcquireResource(resource->size())),
lock_(new ResourceProvider::ScopedWriteLockGpuMemoryBuffer(
resource_provider_,
- raster_resource_->id())) {}
+ raster_resource_->id())),
+ sequence_(0) {}
~RasterBufferImpl() override {
- // First unlock raster resource.
+ // Release write lock in case a copy was never scheduled.
lock_.reset();
- // Copy contents of raster resource to |resource_|.
- resource_provider_->CopyResource(raster_resource_->id(), resource_->id());
+ // Make sure any scheduled copy operations are issued before we release the
+ // raster resource.
+ if (sequence_)
+ worker_pool_->AdvanceLastIssuedCopyTo(sequence_);
// Return raster resource to pool so it can be used by another RasterBuffer
// instance.
@@ -54,28 +60,46 @@
return;
RasterWorkerPool::PlaybackToMemory(gpu_memory_buffer->Map(),
- resource_->format(),
- resource_->size(),
+ raster_resource_->format(),
+ raster_resource_->size(),
gpu_memory_buffer->GetStride(),
picture_pile,
rect,
scale,
stats);
gpu_memory_buffer->Unmap();
+
+ sequence_ = worker_pool_->ScheduleCopyOnWorkerThread(
+ lock_.Pass(), raster_resource_.get(), resource_);
}
private:
+ OneCopyRasterWorkerPool* worker_pool_;
ResourceProvider* resource_provider_;
ResourcePool* resource_pool_;
const Resource* resource_;
scoped_ptr<ScopedResource> raster_resource_;
scoped_ptr<ResourceProvider::ScopedWriteLockGpuMemoryBuffer> lock_;
+ CopySequenceNumber sequence_;
DISALLOW_COPY_AND_ASSIGN(RasterBufferImpl);
};
+// Flush interval when performing copy operations.
+const int kCopyFlushPeriod = 4;
+
} // namespace
+OneCopyRasterWorkerPool::CopyOperation::CopyOperation(
+ scoped_ptr<ResourceProvider::ScopedWriteLockGpuMemoryBuffer> write_lock,
+ ResourceProvider::ResourceId src,
+ ResourceProvider::ResourceId dst)
+ : write_lock(write_lock.Pass()), src(src), dst(dst) {
+}
+
+OneCopyRasterWorkerPool::CopyOperation::~CopyOperation() {
+}
+
// static
scoped_ptr<RasterWorkerPool> OneCopyRasterWorkerPool::Create(
base::SequencedTaskRunner* task_runner,
@@ -103,6 +127,10 @@
context_provider_(context_provider),
resource_provider_(resource_provider),
resource_pool_(resource_pool),
+ last_issued_copy_operation_(0),
+ last_flushed_copy_operation_(0),
+ next_copy_operation_sequence_(1),
+ weak_ptr_factory_(this),
raster_finished_weak_ptr_factory_(this) {
DCHECK(context_provider_);
}
@@ -201,6 +229,7 @@
task_graph_runner_->CollectCompletedTasks(namespace_token_,
&completed_tasks_);
+
for (Task::Vector::const_iterator it = completed_tasks_.begin();
it != completed_tasks_.end();
++it) {
@@ -213,15 +242,13 @@
task->RunReplyOnOriginThread();
}
completed_tasks_.clear();
-
- context_provider_->ContextGL()->ShallowFlushCHROMIUM();
}
scoped_ptr<RasterBuffer> OneCopyRasterWorkerPool::AcquireBufferForRaster(
const Resource* resource) {
DCHECK_EQ(resource->format(), resource_pool_->resource_format());
return make_scoped_ptr<RasterBuffer>(
- new RasterBufferImpl(resource_provider_, resource_pool_, resource));
+ new RasterBufferImpl(this, resource_provider_, resource_pool_, resource));
}
void OneCopyRasterWorkerPool::ReleaseBufferForRaster(
@@ -229,6 +256,55 @@
// Nothing to do here. RasterBufferImpl destructor cleans up after itself.
}
+CopySequenceNumber OneCopyRasterWorkerPool::ScheduleCopyOnWorkerThread(
+ scoped_ptr<ResourceProvider::ScopedWriteLockGpuMemoryBuffer> write_lock,
+ const Resource* src,
+ const Resource* dst) {
+ CopySequenceNumber sequence;
+
+ {
+ base::AutoLock lock(lock_);
+
+ sequence = next_copy_operation_sequence_++;
+
+ pending_copy_operations_.push_back(make_scoped_ptr(
+ new CopyOperation(write_lock.Pass(), src->id(), dst->id())));
+ }
+
+ // Post task that will advance last flushed copy operation to |sequence|
+ // if we have reached the flush period.
+ if ((sequence % kCopyFlushPeriod) == 0) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&OneCopyRasterWorkerPool::AdvanceLastFlushedCopyTo,
+ weak_ptr_factory_.GetWeakPtr(),
+ sequence));
+ }
+
+ return sequence;
+}
+
+void OneCopyRasterWorkerPool::AdvanceLastIssuedCopyTo(
+ CopySequenceNumber sequence) {
+ if (last_issued_copy_operation_ >= sequence)
+ return;
+
+ IssueCopyOperations(sequence - last_issued_copy_operation_);
+ last_issued_copy_operation_ = sequence;
+}
+
+void OneCopyRasterWorkerPool::AdvanceLastFlushedCopyTo(
+ CopySequenceNumber sequence) {
+ if (last_flushed_copy_operation_ >= sequence)
+ return;
+
+ AdvanceLastIssuedCopyTo(sequence);
+
+ // Flush all issued copy operations.
+ context_provider_->ContextGL()->ShallowFlushCHROMIUM();
+ last_flushed_copy_operation_ = last_issued_copy_operation_;
+}
+
void OneCopyRasterWorkerPool::OnRasterFinished(TaskSet task_set) {
TRACE_EVENT1(
"cc", "OneCopyRasterWorkerPool::OnRasterFinished", "task_set", task_set);
@@ -244,6 +320,32 @@
client_->DidFinishRunningTasks(task_set);
}
+void OneCopyRasterWorkerPool::IssueCopyOperations(int64 count) {
+ TRACE_EVENT1(
+ "cc", "OneCopyRasterWorkerPool::IssueCopyOperations", "count", count);
+
+ CopyOperation::Deque copy_operations;
+
+ {
+ base::AutoLock lock(lock_);
+
+ for (int64 i = 0; i < count; ++i) {
+ DCHECK(!pending_copy_operations_.empty());
+ copy_operations.push_back(pending_copy_operations_.take_front());
+ }
+ }
+
+ while (!copy_operations.empty()) {
+ scoped_ptr<CopyOperation> copy_operation = copy_operations.take_front();
+
+ // Remove the write lock.
+ copy_operation->write_lock.reset();
+
+ // Copy contents of source resource to destination resource.
+ resource_provider_->CopyResource(copy_operation->src, copy_operation->dst);
+ }
+}
+
scoped_refptr<base::debug::ConvertableToTraceFormat>
OneCopyRasterWorkerPool::StateAsValue() const {
scoped_refptr<base::debug::TracedValue> state =
diff --git a/cc/resources/one_copy_raster_worker_pool.h b/cc/resources/one_copy_raster_worker_pool.h
index c84b559..7372fb7 100644
--- a/cc/resources/one_copy_raster_worker_pool.h
+++ b/cc/resources/one_copy_raster_worker_pool.h
@@ -6,10 +6,13 @@
#define CC_RESOURCES_ONE_COPY_RASTER_WORKER_POOL_H_
#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
#include "base/values.h"
+#include "cc/base/scoped_ptr_deque.h"
#include "cc/output/context_provider.h"
#include "cc/resources/raster_worker_pool.h"
#include "cc/resources/rasterizer.h"
+#include "cc/resources/resource_provider.h"
namespace base {
namespace debug {
@@ -21,7 +24,8 @@
namespace cc {
class ResourcePool;
class ResourceProvider;
-class ScopedResource;
+
+typedef int64 CopySequenceNumber;
class CC_EXPORT OneCopyRasterWorkerPool : public RasterWorkerPool,
public Rasterizer,
@@ -50,6 +54,17 @@
const Resource* resource) override;
void ReleaseBufferForRaster(scoped_ptr<RasterBuffer> buffer) override;
+ // Schedule copy of |src| resource to |dst| resource. Returns a non-zero
+ // sequence number for this copy operation.
+ CopySequenceNumber ScheduleCopyOnWorkerThread(
+ scoped_ptr<ResourceProvider::ScopedWriteLockGpuMemoryBuffer> write_lock,
+ const Resource* src,
+ const Resource* dst);
+
+ // Issues copy operations until |sequence| has been processed. This will
+ // return immediately if |sequence| has already been processed.
+ void AdvanceLastIssuedCopyTo(CopySequenceNumber sequence);
+
protected:
OneCopyRasterWorkerPool(base::SequencedTaskRunner* task_runner,
TaskGraphRunner* task_graph_runner,
@@ -58,7 +73,23 @@
ResourcePool* resource_pool);
private:
+ struct CopyOperation {
+ typedef ScopedPtrDeque<CopyOperation> Deque;
+
+ CopyOperation(
+ scoped_ptr<ResourceProvider::ScopedWriteLockGpuMemoryBuffer> write_lock,
+ ResourceProvider::ResourceId src,
+ ResourceProvider::ResourceId dst);
+ ~CopyOperation();
+
+ scoped_ptr<ResourceProvider::ScopedWriteLockGpuMemoryBuffer> write_lock;
+ ResourceProvider::ResourceId src;
+ ResourceProvider::ResourceId dst;
+ };
+
void OnRasterFinished(TaskSet task_set);
+ void AdvanceLastFlushedCopyTo(CopySequenceNumber sequence);
+ void IssueCopyOperations(int64 count);
scoped_refptr<base::debug::ConvertableToTraceFormat> StateAsValue() const;
void StagingStateAsValueInto(base::debug::TracedValue* staging_state) const;
@@ -71,12 +102,22 @@
ResourcePool* resource_pool_;
TaskSetCollection raster_pending_;
scoped_refptr<RasterizerTask> raster_finished_tasks_[kNumberOfTaskSets];
+ CopySequenceNumber last_issued_copy_operation_;
+ CopySequenceNumber last_flushed_copy_operation_;
// Task graph used when scheduling tasks and vector used to gather
// completed tasks.
TaskGraph graph_;
Task::Vector completed_tasks_;
+ base::Lock lock_;
+ // |lock_| must be acquired when accessing the following members.
+ CopyOperation::Deque pending_copy_operations_;
+ CopySequenceNumber next_copy_operation_sequence_;
+
+ base::WeakPtrFactory<OneCopyRasterWorkerPool> weak_ptr_factory_;
+ // "raster finished" tasks need their own factory as they need to be
+ // canceled when ScheduleTasks() is called.
base::WeakPtrFactory<OneCopyRasterWorkerPool>
raster_finished_weak_ptr_factory_;
diff --git a/cc/resources/picture.cc b/cc/resources/picture.cc
index d9b10be..c35da96 100644
--- a/cc/resources/picture.cc
+++ b/cc/resources/picture.cc
@@ -18,17 +18,16 @@
#include "cc/debug/traced_value.h"
#include "cc/layers/content_layer_client.h"
#include "skia/ext/pixel_ref_utils.h"
+#include "third_party/skia/include/core/SkBBHFactory.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkData.h"
-#include "third_party/skia/include/core/SkDrawFilter.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkStream.h"
#include "third_party/skia/include/utils/SkNullCanvas.h"
-#include "third_party/skia/include/utils/SkPictureUtils.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/skia_util.h"
namespace cc {
diff --git a/cc/resources/picture.h b/cc/resources/picture.h
index e0b6441..1677309 100644
--- a/cc/resources/picture.h
+++ b/cc/resources/picture.h
@@ -21,7 +21,7 @@
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkBBHFactory.h"
#include "third_party/skia/include/core/SkPicture.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
class SkPixelRef;
diff --git a/cc/resources/picture_layer_tiling.cc b/cc/resources/picture_layer_tiling.cc
index 38e2dc4..4dc46e0 100644
--- a/cc/resources/picture_layer_tiling.cc
+++ b/cc/resources/picture_layer_tiling.cc
@@ -15,10 +15,10 @@
#include "cc/base/math_util.h"
#include "cc/resources/tile.h"
#include "cc/resources/tile_priority.h"
-#include "ui/gfx/point_conversions.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/safe_integer_conversions.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/resources/picture_layer_tiling.h b/cc/resources/picture_layer_tiling.h
index 5da4c8d..f692702 100644
--- a/cc/resources/picture_layer_tiling.h
+++ b/cc/resources/picture_layer_tiling.h
@@ -18,7 +18,7 @@
#include "cc/resources/tile.h"
#include "cc/resources/tile_priority.h"
#include "cc/trees/occlusion.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace base {
namespace debug {
diff --git a/cc/resources/picture_layer_tiling_set.h b/cc/resources/picture_layer_tiling_set.h
index 6f61243..ddde1d7 100644
--- a/cc/resources/picture_layer_tiling_set.h
+++ b/cc/resources/picture_layer_tiling_set.h
@@ -8,7 +8,7 @@
#include "cc/base/region.h"
#include "cc/base/scoped_ptr_vector.h"
#include "cc/resources/picture_layer_tiling.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace base {
namespace debug {
diff --git a/cc/resources/picture_layer_tiling_set_unittest.cc b/cc/resources/picture_layer_tiling_set_unittest.cc
index 3d249f3..2005175 100644
--- a/cc/resources/picture_layer_tiling_set_unittest.cc
+++ b/cc/resources/picture_layer_tiling_set_unittest.cc
@@ -7,15 +7,13 @@
#include <map>
#include <vector>
-#include "cc/resources/resource_pool.h"
#include "cc/resources/resource_provider.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_picture_layer_tiling_client.h"
-#include "cc/test/fake_tile_manager_client.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/resources/picture_layer_tiling_unittest.cc b/cc/resources/picture_layer_tiling_unittest.cc
index 07da353..2952c6a 100644
--- a/cc/resources/picture_layer_tiling_unittest.cc
+++ b/cc/resources/picture_layer_tiling_unittest.cc
@@ -15,8 +15,9 @@
#include "cc/test/test_context_provider.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index 93464f6..372bcbd 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -10,9 +10,8 @@
#include "cc/base/region.h"
#include "cc/debug/rendering_stats_instrumentation.h"
-#include "cc/resources/picture_pile_impl.h"
#include "cc/resources/raster_worker_pool.h"
-#include "cc/resources/tile_priority.h"
+#include "skia/ext/analysis_canvas.h"
namespace {
// Layout pixel buffer around the visible layer rect to record. Any base
diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h
index d68387f..595ded4 100644
--- a/cc/resources/picture_pile.h
+++ b/cc/resources/picture_pile.h
@@ -6,7 +6,7 @@
#define CC_RESOURCES_PICTURE_PILE_H_
#include "cc/resources/picture_pile_base.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
class PicturePileImpl;
diff --git a/cc/resources/picture_pile_base.cc b/cc/resources/picture_pile_base.cc
index d850e41..45db791 100644
--- a/cc/resources/picture_pile_base.cc
+++ b/cc/resources/picture_pile_base.cc
@@ -11,10 +11,9 @@
#include "base/debug/trace_event_argument.h"
#include "base/logging.h"
#include "base/values.h"
-#include "cc/base/math_util.h"
#include "cc/debug/traced_value.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace {
// Dimensions of the tiles in this picture pile as well as the dimensions of
diff --git a/cc/resources/picture_pile_base.h b/cc/resources/picture_pile_base.h
index 991a29c..bb9116c 100644
--- a/cc/resources/picture_pile_base.h
+++ b/cc/resources/picture_pile_base.h
@@ -15,7 +15,7 @@
#include "cc/base/region.h"
#include "cc/base/tiling_data.h"
#include "cc/resources/picture.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace base {
namespace debug {
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc
index e19dd2c..e8cee1e 100644
--- a/cc/resources/picture_pile_impl.cc
+++ b/cc/resources/picture_pile_impl.cc
@@ -12,10 +12,7 @@
#include "skia/ext/analysis_canvas.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
-#include "third_party/skia/include/core/SkSize.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/skia_util.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
diff --git a/cc/resources/picture_pile_impl_unittest.cc b/cc/resources/picture_pile_impl_unittest.cc
index d0daa6b..0ced279 100644
--- a/cc/resources/picture_pile_impl_unittest.cc
+++ b/cc/resources/picture_pile_impl_unittest.cc
@@ -10,8 +10,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/core/SkShader.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc
index 881055f..537872d 100644
--- a/cc/resources/picture_pile_unittest.cc
+++ b/cc/resources/picture_pile_unittest.cc
@@ -9,8 +9,8 @@
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_rendering_stats_instrumentation.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/resources/picture_unittest.cc b/cc/resources/picture_unittest.cc
index 4abeceb..07faf0c 100644
--- a/cc/resources/picture_unittest.cc
+++ b/cc/resources/picture_unittest.cc
@@ -11,10 +11,8 @@
#include "cc/test/skia_common.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBBHFactory.h"
-#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkGraphics.h"
-#include "third_party/skia/include/core/SkPixelRef.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/skia_util.h"
namespace cc {
diff --git a/cc/resources/prioritized_resource.h b/cc/resources/prioritized_resource.h
index 7920f81..e3bb2bc 100644
--- a/cc/resources/prioritized_resource.h
+++ b/cc/resources/prioritized_resource.h
@@ -9,12 +9,11 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
-#include "cc/resources/priority_calculator.h"
#include "cc/resources/resource.h"
#include "cc/resources/resource_provider.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
-#include "ui/gfx/vector2d.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector2d.h"
namespace cc {
diff --git a/cc/resources/prioritized_resource_manager.cc b/cc/resources/prioritized_resource_manager.cc
index 54ed308..657c14f 100644
--- a/cc/resources/prioritized_resource_manager.cc
+++ b/cc/resources/prioritized_resource_manager.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include "base/debug/trace_event.h"
-#include "base/stl_util.h"
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/priority_calculator.h"
#include "cc/trees/proxy.h"
diff --git a/cc/resources/prioritized_resource_manager.h b/cc/resources/prioritized_resource_manager.h
index 681fee3..b7583f4 100644
--- a/cc/resources/prioritized_resource_manager.h
+++ b/cc/resources/prioritized_resource_manager.h
@@ -16,12 +16,10 @@
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/priority_calculator.h"
#include "cc/resources/resource.h"
-#include "cc/trees/proxy.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
-class PriorityCalculator;
class Proxy;
class CC_EXPORT PrioritizedResourceManager {
diff --git a/cc/resources/priority_calculator.cc b/cc/resources/priority_calculator.cc
index 2a0cd18..5f5916f 100644
--- a/cc/resources/priority_calculator.cc
+++ b/cc/resources/priority_calculator.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/resources/raster_tile_priority_queue.cc b/cc/resources/raster_tile_priority_queue.cc
index daf4567..667d54d 100644
--- a/cc/resources/raster_tile_priority_queue.cc
+++ b/cc/resources/raster_tile_priority_queue.cc
@@ -17,7 +17,7 @@
const RasterTilePriorityQueue::PairedPictureLayerQueue* a,
const RasterTilePriorityQueue::PairedPictureLayerQueue* b) const {
// Note that in this function, we have to return true if and only if
- // b is strictly lower priority than a. Note that for the sake of
+ // a is strictly lower priority than b. Note that for the sake of
// completeness, empty queue is considered to have lowest priority.
if (a->IsEmpty() || b->IsEmpty())
return b->IsEmpty() < a->IsEmpty();
@@ -39,6 +39,22 @@
b_tile->priority_for_tree_priority(tree_priority_);
bool prioritize_low_res = tree_priority_ == SMOOTHNESS_TAKES_PRIORITY;
+ // In smoothness mode, we should return pending NOW tiles before active
+ // EVENTUALLY tiles. So if both priorities here are eventually, we need to
+ // check the pending priority.
+ if (prioritize_low_res &&
+ a_priority.priority_bin == TilePriority::EVENTUALLY &&
+ b_priority.priority_bin == TilePriority::EVENTUALLY) {
+ bool a_is_pending_now =
+ a_tile->priority(PENDING_TREE).priority_bin == TilePriority::NOW;
+ bool b_is_pending_now =
+ b_tile->priority(PENDING_TREE).priority_bin == TilePriority::NOW;
+ if (a_is_pending_now || b_is_pending_now)
+ return a_is_pending_now < b_is_pending_now;
+
+ // In case neither one is pending now, fall through.
+ }
+
// If the bin is the same but the resolution is not, then the order will be
// determined by whether we prioritize low res or not.
// TODO(vmpstr): Remove this when TilePriority is no longer a member of Tile
@@ -56,6 +72,7 @@
return b_priority.resolution == LOW_RESOLUTION;
return b_priority.resolution == HIGH_RESOLUTION;
}
+
return b_priority.IsHigherPriorityThan(a_priority);
}
diff --git a/cc/resources/resource.h b/cc/resources/resource.h
index 24cb88a..707d77e 100644
--- a/cc/resources/resource.h
+++ b/cc/resources/resource.h
@@ -8,7 +8,7 @@
#include "cc/base/cc_export.h"
#include "cc/resources/resource_provider.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 550475b..a37789a 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -27,9 +27,9 @@
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "ui/gfx/frame_time.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/gpu_memory_buffer.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
using gpu::gles2::GLES2Interface;
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index fbec8d1..2e2fecb 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -31,7 +31,7 @@
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
class GrContext;
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index 2c585c4..4efe74d 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -29,8 +29,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gpu_memory_buffer.h"
-#include "ui/gfx/rect.h"
using testing::Mock;
using testing::NiceMock;
diff --git a/cc/resources/resource_update.h b/cc/resources/resource_update.h
index 1eddf83..d491141 100644
--- a/cc/resources/resource_update.h
+++ b/cc/resources/resource_update.h
@@ -6,8 +6,8 @@
#define CC_RESOURCES_RESOURCE_UPDATE_H_
#include "cc/base/cc_export.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
class SkBitmap;
diff --git a/cc/resources/scoped_ui_resource.h b/cc/resources/scoped_ui_resource.h
index f7def7f..0e5a02c 100644
--- a/cc/resources/scoped_ui_resource.h
+++ b/cc/resources/scoped_ui_resource.h
@@ -9,7 +9,6 @@
#include "cc/base/cc_export.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/resources/ui_resource_client.h"
-#include "ui/gfx/size.h"
namespace cc {
diff --git a/cc/resources/shared_bitmap.h b/cc/resources/shared_bitmap.h
index a90e47a..ca12710 100644
--- a/cc/resources/shared_bitmap.h
+++ b/cc/resources/shared_bitmap.h
@@ -10,7 +10,7 @@
#include "base/memory/shared_memory.h"
#include "cc/base/cc_export.h"
#include "gpu/command_buffer/common/mailbox.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace base { class SharedMemory; }
diff --git a/cc/resources/shared_bitmap_manager.h b/cc/resources/shared_bitmap_manager.h
index e6e49af..fe61b09 100644
--- a/cc/resources/shared_bitmap_manager.h
+++ b/cc/resources/shared_bitmap_manager.h
@@ -8,7 +8,7 @@
#include "base/basictypes.h"
#include "cc/base/cc_export.h"
#include "cc/resources/shared_bitmap.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/resources/texture_mailbox.cc b/cc/resources/texture_mailbox.cc
index 90ce6be..92736f4 100644
--- a/cc/resources/texture_mailbox.cc
+++ b/cc/resources/texture_mailbox.cc
@@ -6,7 +6,6 @@
#include "base/logging.h"
#include "cc/resources/shared_bitmap.h"
-#include "third_party/khronos/GLES2/gl2.h"
namespace cc {
diff --git a/cc/resources/texture_mailbox.h b/cc/resources/texture_mailbox.h
index 4626dd3..4a0b76a 100644
--- a/cc/resources/texture_mailbox.h
+++ b/cc/resources/texture_mailbox.h
@@ -7,11 +7,10 @@
#include <string>
-#include "base/callback.h"
#include "base/memory/shared_memory.h"
#include "cc/base/cc_export.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/resources/texture_uploader.cc b/cc/resources/texture_uploader.cc
index 8c7010b..9526fcb 100644
--- a/cc/resources/texture_uploader.cc
+++ b/cc/resources/texture_uploader.cc
@@ -10,14 +10,13 @@
#include "base/debug/trace_event.h"
#include "base/metrics/histogram.h"
#include "cc/base/util.h"
-#include "cc/resources/prioritized_resource.h"
#include "cc/resources/resource.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
using gpu::gles2::GLES2Interface;
diff --git a/cc/resources/tile.h b/cc/resources/tile.h
index e61b5c5..a712a75 100644
--- a/cc/resources/tile.h
+++ b/cc/resources/tile.h
@@ -6,14 +6,12 @@
#define CC_RESOURCES_TILE_H_
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
#include "cc/base/ref_counted_managed.h"
#include "cc/resources/managed_tile_state.h"
#include "cc/resources/picture_pile_impl.h"
#include "cc/resources/tile_priority.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc
index cb9d766..77c4df6 100644
--- a/cc/resources/tile_manager.cc
+++ b/cc/resources/tile_manager.cc
@@ -20,7 +20,7 @@
#include "cc/resources/raster_buffer.h"
#include "cc/resources/rasterizer.h"
#include "cc/resources/tile.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
namespace {
@@ -193,8 +193,6 @@
DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl);
};
-const size_t kScheduledRasterTasksLimit = 32u;
-
} // namespace
RasterTaskCompletionStats::RasterTaskCompletionStats()
@@ -215,12 +213,14 @@
base::SequencedTaskRunner* task_runner,
ResourcePool* resource_pool,
Rasterizer* rasterizer,
- RenderingStatsInstrumentation* rendering_stats_instrumentation) {
+ RenderingStatsInstrumentation* rendering_stats_instrumentation,
+ size_t scheduled_raster_task_limit) {
return make_scoped_ptr(new TileManager(client,
task_runner,
resource_pool,
rasterizer,
- rendering_stats_instrumentation));
+ rendering_stats_instrumentation,
+ scheduled_raster_task_limit));
}
TileManager::TileManager(
@@ -228,11 +228,13 @@
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
ResourcePool* resource_pool,
Rasterizer* rasterizer,
- RenderingStatsInstrumentation* rendering_stats_instrumentation)
+ RenderingStatsInstrumentation* rendering_stats_instrumentation,
+ size_t scheduled_raster_task_limit)
: client_(client),
task_runner_(task_runner),
resource_pool_(resource_pool),
rasterizer_(rasterizer),
+ scheduled_raster_task_limit_(scheduled_raster_task_limit),
all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
rendering_stats_instrumentation_(rendering_stats_instrumentation),
did_initialize_visible_tile_(false),
@@ -570,7 +572,7 @@
// We won't be able to schedule this tile, so break out early.
if (tiles_that_need_to_be_rasterized->size() >=
- kScheduledRasterTasksLimit) {
+ scheduled_raster_task_limit_) {
all_tiles_that_need_to_be_rasterized_are_scheduled_ = false;
break;
}
diff --git a/cc/resources/tile_manager.h b/cc/resources/tile_manager.h
index 2abf177..6f579f9 100644
--- a/cc/resources/tile_manager.h
+++ b/cc/resources/tile_manager.h
@@ -98,7 +98,8 @@
base::SequencedTaskRunner* task_runner,
ResourcePool* resource_pool,
Rasterizer* rasterizer,
- RenderingStatsInstrumentation* rendering_stats_instrumentation);
+ RenderingStatsInstrumentation* rendering_stats_instrumentation,
+ size_t scheduled_raster_task_limit);
~TileManager() override;
void ManageTiles(const GlobalStateThatImpactsTilePriority& state);
@@ -163,7 +164,8 @@
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
ResourcePool* resource_pool,
Rasterizer* rasterizer,
- RenderingStatsInstrumentation* rendering_stats_instrumentation);
+ RenderingStatsInstrumentation* rendering_stats_instrumentation,
+ size_t scheduled_raster_task_limit);
void FreeResourcesForReleasedTiles();
void CleanUpReleasedTiles();
@@ -236,6 +238,7 @@
ResourcePool* resource_pool_;
Rasterizer* rasterizer_;
GlobalStateThatImpactsTilePriority global_state_;
+ const size_t scheduled_raster_task_limit_;
typedef base::hash_map<Tile::Id, Tile*> TileMap;
TileMap tiles_;
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc
index 3671081..398474b 100644
--- a/cc/resources/tile_manager_unittest.cc
+++ b/cc/resources/tile_manager_unittest.cc
@@ -322,6 +322,63 @@
EXPECT_GE(increasing_distance_tiles, 3 * tile_count / 4);
}
+TEST_F(TileManagerTilePriorityQueueTest, ActivationComesBeforeEventually) {
+ SetupDefaultTrees(gfx::Size(1000, 1000));
+
+ active_layer_->CreateDefaultTilingsAndTiles();
+ pending_layer_->CreateDefaultTilingsAndTiles();
+
+ // Create a pending child layer.
+ gfx::Size tile_size(256, 256);
+ scoped_refptr<FakePicturePileImpl> pending_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, gfx::Size(1000, 1000));
+ scoped_ptr<FakePictureLayerImpl> pending_child =
+ FakePictureLayerImpl::CreateWithPile(
+ host_impl_.pending_tree(), id_ + 1, pending_pile);
+ pending_layer_->AddChild(pending_child.Pass());
+ FakePictureLayerImpl* pending_child_raw = static_cast<FakePictureLayerImpl*>(
+ host_impl_.pending_tree()->LayerById(id_ + 1));
+ ASSERT_TRUE(pending_child_raw);
+
+ pending_child_raw->SetDrawsContent(true);
+ pending_child_raw->DoPostCommitInitializationIfNeeded();
+ pending_child_raw->CreateDefaultTilingsAndTiles();
+ ASSERT_TRUE(pending_child_raw->HighResTiling());
+
+ // Set a small viewport, so we have soon and eventually tiles.
+ gfx::Rect viewport(200, 200);
+ active_layer_->draw_properties().visible_content_rect = viewport;
+ active_layer_->UpdateTiles(Occlusion(), false);
+ pending_layer_->draw_properties().visible_content_rect = viewport;
+ pending_layer_->UpdateTiles(Occlusion(), false);
+ pending_child_raw->draw_properties().visible_content_rect = viewport;
+ pending_child_raw->UpdateTiles(Occlusion(), false);
+
+ RasterTilePriorityQueue queue;
+ host_impl_.SetRequiresHighResToDraw();
+ host_impl_.BuildRasterQueue(&queue, SMOOTHNESS_TAKES_PRIORITY);
+ EXPECT_FALSE(queue.IsEmpty());
+
+ // Get all the tiles that are NOW or SOON and make sure they are ready to
+ // draw.
+ std::vector<Tile*> all_tiles;
+ while (!queue.IsEmpty()) {
+ Tile* tile = queue.Top();
+ if (tile->combined_priority().priority_bin >= TilePriority::EVENTUALLY)
+ break;
+
+ all_tiles.push_back(tile);
+ queue.Pop();
+ }
+
+ tile_manager()->InitializeTilesWithResourcesForTesting(
+ std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
+
+ // Ensure we can activate.
+ EXPECT_TRUE(pending_layer_->AllTilesRequiredForActivationAreReadyToDraw());
+ EXPECT_TRUE(pending_child_raw->AllTilesRequiredForActivationAreReadyToDraw());
+}
+
TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) {
SetupDefaultTrees(gfx::Size(1000, 1000));
diff --git a/cc/resources/tile_priority.h b/cc/resources/tile_priority.h
index 433abad..d161057 100644
--- a/cc/resources/tile_priority.h
+++ b/cc/resources/tile_priority.h
@@ -9,12 +9,9 @@
#include <limits>
#include <string>
-#include "base/memory/ref_counted.h"
+#include "base/debug/trace_event_argument.h"
#include "base/memory/scoped_ptr.h"
-#include "cc/resources/picture_pile.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
+#include "cc/base/cc_export.h"
namespace base {
class Value;
diff --git a/cc/resources/transferable_resource.cc b/cc/resources/transferable_resource.cc
index a375f2e..558d6e8 100644
--- a/cc/resources/transferable_resource.cc
+++ b/cc/resources/transferable_resource.cc
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/logging.h"
#include "cc/resources/returned_resource.h"
#include "cc/resources/transferable_resource.h"
diff --git a/cc/resources/transferable_resource.h b/cc/resources/transferable_resource.h
index bd6b060..03b47e6 100644
--- a/cc/resources/transferable_resource.h
+++ b/cc/resources/transferable_resource.h
@@ -11,7 +11,7 @@
#include "cc/base/cc_export.h"
#include "cc/resources/resource_format.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/resources/ui_resource_bitmap.h b/cc/resources/ui_resource_bitmap.h
index 09403a3..ec65113 100644
--- a/cc/resources/ui_resource_bitmap.h
+++ b/cc/resources/ui_resource_bitmap.h
@@ -10,8 +10,7 @@
#include "cc/base/cc_export.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkPixelRef.h"
-#include "third_party/skia/include/core/SkTypes.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
class SkBitmap;
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index 4d0e125..01a6042 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -14,7 +14,7 @@
#include "media/filters/skcanvas_video_renderer.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
diff --git a/cc/resources/video_resource_updater.h b/cc/resources/video_resource_updater.h
index e9b924b..99800b4 100644
--- a/cc/resources/video_resource_updater.h
+++ b/cc/resources/video_resource_updater.h
@@ -9,13 +9,12 @@
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/resources/release_callback_impl.h"
#include "cc/resources/resource_format.h"
#include "cc/resources/texture_mailbox.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace media {
class SkCanvasVideoRenderer;
diff --git a/cc/surfaces/surface.h b/cc/surfaces/surface.h
index c55dfe8..ffa5042 100644
--- a/cc/surfaces/surface.h
+++ b/cc/surfaces/surface.h
@@ -18,7 +18,7 @@
#include "cc/quads/render_pass_id.h"
#include "cc/surfaces/surface_id.h"
#include "cc/surfaces/surfaces_export.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace ui {
struct LatencyInfo;
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
index f6bf6d6..e3574e5 100644
--- a/cc/surfaces/surface_aggregator.cc
+++ b/cc/surfaces/surface_aggregator.cc
@@ -40,7 +40,7 @@
SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
ResourceProvider* provider)
- : manager_(manager), provider_(provider) {
+ : manager_(manager), provider_(provider), next_render_pass_id_(1) {
DCHECK(manager_);
}
@@ -48,25 +48,23 @@
class SurfaceAggregator::RenderPassIdAllocator {
public:
- explicit RenderPassIdAllocator(SurfaceId surface_id)
- : surface_id_(surface_id), next_index_(1) {}
+ explicit RenderPassIdAllocator(int* next_index) : next_index_(next_index) {}
~RenderPassIdAllocator() {}
void AddKnownPass(RenderPassId id) {
if (id_to_index_map_.find(id) != id_to_index_map_.end())
return;
- id_to_index_map_[id] = next_index_++;
+ id_to_index_map_[id] = (*next_index_)++;
}
RenderPassId Remap(RenderPassId id) {
DCHECK(id_to_index_map_.find(id) != id_to_index_map_.end());
- return RenderPassId(surface_id_.id, id_to_index_map_[id]);
+ return RenderPassId(1, id_to_index_map_[id]);
}
private:
base::hash_map<RenderPassId, int> id_to_index_map_;
- SurfaceId surface_id_;
- int next_index_;
+ int* next_index_;
DISALLOW_COPY_AND_ASSIGN(RenderPassIdAllocator);
};
@@ -82,7 +80,7 @@
SurfaceId surface_id) {
RenderPassIdAllocator* allocator = render_pass_allocator_map_.get(surface_id);
if (!allocator) {
- allocator = new RenderPassIdAllocator(surface_id);
+ allocator = new RenderPassIdAllocator(&next_render_pass_id_);
render_pass_allocator_map_.set(surface_id, make_scoped_ptr(allocator));
}
allocator->AddKnownPass(surface_local_pass_id);
diff --git a/cc/surfaces/surface_aggregator.h b/cc/surfaces/surface_aggregator.h
index 64c10e1..31d35dd 100644
--- a/cc/surfaces/surface_aggregator.h
+++ b/cc/surfaces/surface_aggregator.h
@@ -66,6 +66,7 @@
typedef base::ScopedPtrHashMap<SurfaceId, RenderPassIdAllocator>
RenderPassIdAllocatorMap;
RenderPassIdAllocatorMap render_pass_allocator_map_;
+ int next_render_pass_id_;
typedef base::hash_map<SurfaceId, int> SurfaceToResourceChildIdMap;
SurfaceToResourceChildIdMap surface_id_to_resource_child_id_;
diff --git a/cc/surfaces/surface_aggregator_test_helpers.h b/cc/surfaces/surface_aggregator_test_helpers.h
index 4e02472..6f3a342 100644
--- a/cc/surfaces/surface_aggregator_test_helpers.h
+++ b/cc/surfaces/surface_aggregator_test_helpers.h
@@ -10,7 +10,7 @@
#include "cc/quads/render_pass_id.h"
#include "cc/surfaces/surface_id.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc
index cea06ea..4771c82 100644
--- a/cc/surfaces/surface_aggregator_unittest.cc
+++ b/cc/surfaces/surface_aggregator_unittest.cc
@@ -67,7 +67,7 @@
class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
public:
- SurfaceAggregatorValidSurfaceTest() : allocator_(1u) {}
+ SurfaceAggregatorValidSurfaceTest() : allocator_(1u), child_allocator_(2u) {}
virtual void SetUp() {
SurfaceAggregatorTest::SetUp();
@@ -96,6 +96,12 @@
TestPassesMatchExpectations(
expected_passes, expected_pass_count, &frame_data->render_pass_list);
+ // Ensure no duplicate pass ids output.
+ std::set<RenderPassId> used_passes;
+ for (auto* pass : frame_data->render_pass_list) {
+ EXPECT_TRUE(used_passes.insert(pass->id).second);
+ }
+
EXPECT_EQ(expected_surface_count,
aggregator_.previous_contained_surfaces().size());
for (size_t i = 0; i < expected_surface_count; i++) {
@@ -133,6 +139,7 @@
protected:
SurfaceId root_surface_id_;
SurfaceIdAllocator allocator_;
+ SurfaceIdAllocator child_allocator_;
};
// Tests that a very simple frame containing only two solid color quads makes it
@@ -153,8 +160,9 @@
test::Quad::SolidColorQuad(SK_ColorLTGRAY)},
{test::Quad::SolidColorQuad(SK_ColorGRAY),
test::Quad::SolidColorQuad(SK_ColorDKGRAY)}};
- test::Pass passes[] = {test::Pass(quads[0], arraysize(quads[0])),
- test::Pass(quads[1], arraysize(quads[1]))};
+ test::Pass passes[] = {
+ test::Pass(quads[0], arraysize(quads[0]), RenderPassId(1, 1)),
+ test::Pass(quads[1], arraysize(quads[1]), RenderPassId(1, 2))};
SubmitFrame(passes, arraysize(passes), root_surface_id_);
@@ -341,7 +349,7 @@
// This tests referencing a surface that has multiple render passes.
TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSurfaceReference) {
- SurfaceId embedded_surface_id = allocator_.GenerateId();
+ SurfaceId embedded_surface_id = child_allocator_.GenerateId();
factory_.Create(embedded_surface_id, SurfaceSize());
RenderPassId pass_ids[] = {RenderPassId(1, 1), RenderPassId(1, 2),
diff --git a/cc/surfaces/surface_factory_unittest.cc b/cc/surfaces/surface_factory_unittest.cc
index 636a5ff..4e0865a 100644
--- a/cc/surfaces/surface_factory_unittest.cc
+++ b/cc/surfaces/surface_factory_unittest.cc
@@ -9,7 +9,7 @@
#include "cc/surfaces/surface_factory_client.h"
#include "cc/surfaces/surface_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
namespace {
diff --git a/cc/surfaces/surface_unittest.cc b/cc/surfaces/surface_unittest.cc
index d2b1dfa..b8990c5 100644
--- a/cc/surfaces/surface_unittest.cc
+++ b/cc/surfaces/surface_unittest.cc
@@ -6,7 +6,7 @@
#include "cc/surfaces/surface_factory.h"
#include "cc/surfaces/surface_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
namespace {
diff --git a/cc/test/fake_content_layer_client.h b/cc/test/fake_content_layer_client.h
index 8f13284..7a7cc44 100644
--- a/cc/test/fake_content_layer_client.h
+++ b/cc/test/fake_content_layer_client.h
@@ -12,7 +12,7 @@
#include "cc/layers/content_layer_client.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkPaint.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/test/fake_picture_layer_tiling_client.h b/cc/test/fake_picture_layer_tiling_client.h
index dece2fc..0e2f645 100644
--- a/cc/test/fake_picture_layer_tiling_client.h
+++ b/cc/test/fake_picture_layer_tiling_client.h
@@ -10,7 +10,7 @@
#include "cc/resources/tile.h"
#include "cc/resources/tile_manager.h"
#include "cc/test/fake_tile_manager_client.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/test/fake_tile_manager.cc b/cc/test/fake_tile_manager.cc
index bebd797..8aa343b 100644
--- a/cc/test/fake_tile_manager.cc
+++ b/cc/test/fake_tile_manager.cc
@@ -5,6 +5,7 @@
#include "cc/test/fake_tile_manager.h"
#include <deque>
+#include <limits>
#include "base/lazy_instance.h"
#include "cc/resources/raster_buffer.h"
@@ -68,7 +69,9 @@
base::MessageLoopProxy::current(),
NULL,
g_fake_rasterizer.Pointer(),
- NULL) {}
+ NULL,
+ std::numeric_limits<size_t>::max()) {
+}
FakeTileManager::FakeTileManager(TileManagerClient* client,
ResourcePool* resource_pool)
@@ -76,7 +79,9 @@
base::MessageLoopProxy::current(),
resource_pool,
g_fake_rasterizer.Pointer(),
- NULL) {}
+ NULL,
+ std::numeric_limits<size_t>::max()) {
+}
FakeTileManager::~FakeTileManager() {}
diff --git a/cc/test/layer_test_common.cc b/cc/test/layer_test_common.cc
index 4ad0aec..a6dfa9c 100644
--- a/cc/test/layer_test_common.cc
+++ b/cc/test/layer_test_common.cc
@@ -13,10 +13,10 @@
#include "cc/test/mock_occlusion_tracker.h"
#include "cc/trees/layer_tree_host_common.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/point_conversions.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc
index 2f293f7..31d30a4 100644
--- a/cc/test/layer_tree_pixel_test.cc
+++ b/cc/test/layer_tree_pixel_test.cc
@@ -28,9 +28,10 @@
LayerTreePixelTest::LayerTreePixelTest()
: pixel_comparator_(new ExactPixelComparator(true)),
- test_type_(GL_WITH_DEFAULT),
+ test_type_(PIXEL_TEST_GL),
pending_texture_mailbox_callbacks_(0),
- impl_side_painting_(true) {}
+ impl_side_painting_(true) {
+}
LayerTreePixelTest::~LayerTreePixelTest() {}
@@ -40,8 +41,7 @@
scoped_ptr<PixelTestOutputSurface> output_surface;
switch (test_type_) {
- case SOFTWARE_WITH_DEFAULT:
- case SOFTWARE_WITH_BITMAP: {
+ case PIXEL_TEST_SOFTWARE: {
scoped_ptr<PixelTestSoftwareOutputDevice> software_output_device(
new PixelTestSoftwareOutputDevice);
software_output_device->set_surface_expansion_size(
@@ -50,9 +50,7 @@
new PixelTestOutputSurface(software_output_device.Pass()));
break;
}
-
- case GL_WITH_DEFAULT:
- case GL_WITH_BITMAP: {
+ case PIXEL_TEST_GL: {
output_surface = make_scoped_ptr(
new PixelTestOutputSurface(new TestInProcessContextProvider));
break;
diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h
index 5c220d5..e6f5cb7 100644
--- a/cc/test/layer_tree_pixel_test.h
+++ b/cc/test/layer_tree_pixel_test.h
@@ -59,10 +59,8 @@
const SkBitmap& bitmap);
enum PixelTestType {
- GL_WITH_DEFAULT,
- GL_WITH_BITMAP,
- SOFTWARE_WITH_DEFAULT,
- SOFTWARE_WITH_BITMAP
+ PIXEL_TEST_GL,
+ PIXEL_TEST_SOFTWARE,
};
void RunPixelTest(PixelTestType type,
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index c19c63f..aabad9b 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -29,7 +29,7 @@
#include "cc/trees/thread_proxy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/frame_time.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
diff --git a/cc/test/pixel_comparator.cc b/cc/test/pixel_comparator.cc
index 73807ed..4067787 100644
--- a/cc/test/pixel_comparator.cc
+++ b/cc/test/pixel_comparator.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include "base/logging.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/test/pixel_test.h b/cc/test/pixel_test.h
index 838acaf..1a6c5ba 100644
--- a/cc/test/pixel_test.h
+++ b/cc/test/pixel_test.h
@@ -8,7 +8,7 @@
#include "cc/quads/render_pass.h"
#include "cc/test/pixel_comparator.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
#include "ui/gl/gl_implementation.h"
#ifndef CC_TEST_PIXEL_TEST_H_
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc
index c923b63..f9def0f 100644
--- a/cc/test/render_pass_test_utils.cc
+++ b/cc/test/render_pass_test_utils.cc
@@ -11,7 +11,7 @@
#include "cc/test/render_pass_test_common.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkImageFilter.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/test/skia_common.cc b/cc/test/skia_common.cc
index d91e707..1ccb2d1 100644
--- a/cc/test/skia_common.cc
+++ b/cc/test/skia_common.cc
@@ -7,7 +7,7 @@
#include "cc/resources/picture.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/skia_util.h"
namespace cc {
diff --git a/cc/test/solid_color_content_layer_client.cc b/cc/test/solid_color_content_layer_client.cc
index 7199f36..237dbb6 100644
--- a/cc/test/solid_color_content_layer_client.cc
+++ b/cc/test/solid_color_content_layer_client.cc
@@ -6,8 +6,8 @@
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPaint.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
namespace cc {
diff --git a/cc/test/test_texture.h b/cc/test/test_texture.h
index 0e50bf0..9265589 100644
--- a/cc/test/test_texture.h
+++ b/cc/test/test_texture.h
@@ -10,7 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/resources/resource_format.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/test/test_web_graphics_context_3d.h b/cc/test/test_web_graphics_context_3d.h
index 0dfba9d..eda6f0f 100644
--- a/cc/test/test_web_graphics_context_3d.h
+++ b/cc/test/test_web_graphics_context_3d.h
@@ -20,7 +20,7 @@
#include "cc/test/ordered_texture_map.h"
#include "cc/test/test_texture.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
extern "C" typedef struct _ClientBuffer* ClientBuffer;
diff --git a/cc/test/tiled_layer_test_common.h b/cc/test/tiled_layer_test_common.h
index 18b9817..abf2de4 100644
--- a/cc/test/tiled_layer_test_common.h
+++ b/cc/test/tiled_layer_test_common.h
@@ -12,8 +12,8 @@
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/resource_provider.h"
#include "cc/resources/resource_update_queue.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
diff --git a/cc/trees/damage_tracker.h b/cc/trees/damage_tracker.h
index 37c4c6e..aafbd03 100644
--- a/cc/trees/damage_tracker.h
+++ b/cc/trees/damage_tracker.h
@@ -9,7 +9,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/layers/layer_lists.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
class SkImageFilter;
diff --git a/cc/trees/layer_sorter.h b/cc/trees/layer_sorter.h
index 7dd980c..b783ded 100644
--- a/cc/trees/layer_sorter.h
+++ b/cc/trees/layer_sorter.h
@@ -11,10 +11,10 @@
#include "base/containers/hash_tables.h"
#include "cc/base/cc_export.h"
#include "cc/layers/layer_impl.h"
-#include "ui/gfx/point3_f.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect_f.h"
-#include "ui/gfx/vector3d_f.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
namespace gfx {
class Transform;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 08fb810..7e17fef 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -40,7 +40,7 @@
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/thread_proxy.h"
#include "cc/trees/tree_synchronizer.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace {
static base::StaticAtomicSequenceNumber s_layer_tree_host_sequence_number;
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index aede050..b312e50 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -39,7 +39,7 @@
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/proxy.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
class AnimationRegistrar;
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 4e80fc6..fd15b52 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -16,7 +16,7 @@
#include "cc/layers/render_surface_impl.h"
#include "cc/trees/layer_sorter.h"
#include "cc/trees/layer_tree_impl.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h
index d4c09c6..089fe29 100644
--- a/cc/trees/layer_tree_host_common.h
+++ b/cc/trees/layer_tree_host_common.h
@@ -13,9 +13,9 @@
#include "cc/base/cc_export.h"
#include "cc/base/scoped_ptr_vector.h"
#include "cc/layers/layer_lists.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/transform.h"
-#include "ui/gfx/vector2d.h"
namespace cc {
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 99e479a..e0436ba 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -33,7 +33,7 @@
#include "cc/trees/single_thread_proxy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/quad_f.h"
+#include "ui/gfx/geometry/quad_f.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 4ccc5fe..4a01ed7 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -72,8 +72,8 @@
#include "gpu/GLES2/gl2extchromium.h"
#include "ui/gfx/frame_time.h"
#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/vector2d_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
namespace cc {
namespace {
@@ -2079,7 +2079,8 @@
task_runner,
resource_pool_.get(),
raster_worker_pool_->AsRasterizer(),
- rendering_stats_instrumentation_);
+ rendering_stats_instrumentation_,
+ settings().scheduled_raster_task_limit);
UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy());
need_to_update_visible_tiles_before_draw_ = false;
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index a6448c1..1fc0989 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -35,7 +35,7 @@
#include "cc/scheduler/draw_result.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 51d86d3..e16cf26 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -58,9 +58,9 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkMallocPixelRef.h"
#include "ui/gfx/frame_time.h"
-#include "ui/gfx/rect_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/vector2d_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
using ::testing::Mock;
using ::testing::Return;
diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc
index bebd612..5bce9dd 100644
--- a/cc/trees/layer_tree_host_pixeltest_blending.cc
+++ b/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -231,7 +231,7 @@
this->impl_side_painting_ = false;
this->force_antialiasing_ = (flags & kUseAntialiasing);
- if ((flags & kUseAntialiasing) && (type == GL_WITH_BITMAP)) {
+ if ((flags & kUseAntialiasing) && (type == PIXEL_TEST_GL)) {
// Anti aliasing causes differences up to 7 pixels at the edges.
int large_error_allowed = 7;
// Blending results might differ with one pixel.
@@ -265,7 +265,7 @@
average_error_allowed_in_bad_pixels,
large_error_allowed,
small_error_allowed));
- } else if ((flags & kUseColorMatrix) && (type == GL_WITH_BITMAP)) {
+ } else if ((flags & kUseColorMatrix) && (type == PIXEL_TEST_GL)) {
float percentage_pixels_error = 100.f;
float percentage_pixels_small_error = 0.f;
float average_error_allowed_in_bad_pixels = 1.f;
@@ -287,11 +287,11 @@
};
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_GL) {
- RunBlendingWithRootPixelTestType(GL_WITH_BITMAP);
+ RunBlendingWithRootPixelTestType(PIXEL_TEST_GL);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_Software) {
- RunBlendingWithRootPixelTestType(SOFTWARE_WITH_BITMAP);
+ RunBlendingWithRootPixelTestType(PIXEL_TEST_SOFTWARE);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithBackgroundFilter) {
@@ -317,90 +317,90 @@
green_lane->SetBlendMode(kBlendModes[i]);
}
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("blending_and_filter.png")));
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_GL) {
- RunBlendingWithTransparentPixelTestType(GL_WITH_BITMAP);
+ RunBlendingWithTransparentPixelTestType(PIXEL_TEST_GL);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_Software) {
- RunBlendingWithTransparentPixelTestType(SOFTWARE_WITH_BITMAP);
+ RunBlendingWithTransparentPixelTestType(PIXEL_TEST_SOFTWARE);
}
// Tests for render passes
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_GL) {
RunBlendingWithRenderPass(
- GL_WITH_BITMAP, FILE_PATH_LITERAL("blending_render_pass.png"), 0);
+ PIXEL_TEST_GL, FILE_PATH_LITERAL("blending_render_pass.png"), 0);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_Software) {
RunBlendingWithRenderPass(
- SOFTWARE_WITH_BITMAP, FILE_PATH_LITERAL("blending_render_pass.png"), 0);
+ PIXEL_TEST_SOFTWARE, FILE_PATH_LITERAL("blending_render_pass.png"), 0);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_GL) {
- RunBlendingWithRenderPass(GL_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass.png"),
kUseAntialiasing);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_Software) {
- RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass.png"),
kUseAntialiasing);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMask_GL) {
- RunBlendingWithRenderPass(GL_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMask_Software) {
- RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskAA_GL) {
- RunBlendingWithRenderPass(GL_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseAntialiasing);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskAA_Software) {
- RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseAntialiasing);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_GL) {
- RunBlendingWithRenderPass(GL_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass_cm.png"),
kUseColorMatrix);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassColorMatrix_Software) {
- RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass_cm.png"),
kUseColorMatrix);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_GL) {
- RunBlendingWithRenderPass(GL_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass_cm.png"),
kUseAntialiasing | kUseColorMatrix);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassColorMatrixAA_Software) {
- RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP,
+ RunBlendingWithRenderPass(PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass_cm.png"),
kUseAntialiasing | kUseColorMatrix);
}
@@ -408,7 +408,7 @@
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskColorMatrix_GL) {
RunBlendingWithRenderPass(
- GL_WITH_BITMAP,
+ PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"),
kUseMasks | kUseColorMatrix);
}
@@ -416,7 +416,7 @@
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskColorMatrix_Software) {
RunBlendingWithRenderPass(
- SOFTWARE_WITH_BITMAP,
+ PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"),
kUseMasks | kUseColorMatrix);
}
@@ -424,7 +424,7 @@
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskColorMatrixAA_GL) {
RunBlendingWithRenderPass(
- GL_WITH_BITMAP,
+ PIXEL_TEST_GL,
FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"),
kUseMasks | kUseAntialiasing | kUseColorMatrix);
}
@@ -432,7 +432,7 @@
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskColorMatrixAA_Software) {
RunBlendingWithRenderPass(
- SOFTWARE_WITH_BITMAP,
+ PIXEL_TEST_SOFTWARE,
FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"),
kUseMasks | kUseAntialiasing | kUseColorMatrix);
}
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index c0438ec..40fa2c3 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -49,7 +49,7 @@
small_error_allowed));
#endif
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("background_filter_blur.png")));
}
@@ -90,10 +90,10 @@
small_error_allowed));
#endif
- RunPixelTest(GL_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "background_filter_blur_outsets.png")));
+ RunPixelTest(
+ PIXEL_TEST_GL,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("background_filter_blur_outsets.png")));
}
TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOffAxis) {
@@ -151,10 +151,10 @@
small_error_allowed));
#endif
- RunPixelTest(GL_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "background_filter_blur_off_axis.png")));
+ RunPixelTest(
+ PIXEL_TEST_GL,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("background_filter_blur_off_axis.png")));
}
class LayerTreeHostFiltersScaledPixelTest
@@ -207,28 +207,20 @@
float device_scale_factor_;
};
-TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_GLBitmap) {
- RunPixelTestType(100, 1.f, GL_WITH_BITMAP);
-}
-
-TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_GLDefault) {
- RunPixelTestType(100, 1.f, GL_WITH_DEFAULT);
+TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_GL) {
+ RunPixelTestType(100, 1.f, PIXEL_TEST_GL);
}
TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_Software) {
- RunPixelTestType(100, 1.f, SOFTWARE_WITH_BITMAP);
+ RunPixelTestType(100, 1.f, PIXEL_TEST_SOFTWARE);
}
-TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_GLBitmap) {
- RunPixelTestType(50, 2.f, GL_WITH_BITMAP);
-}
-
-TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_GLDefault) {
- RunPixelTestType(50, 2.f, GL_WITH_DEFAULT);
+TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_GL) {
+ RunPixelTestType(50, 2.f, PIXEL_TEST_GL);
}
TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_Software) {
- RunPixelTestType(50, 2.f, SOFTWARE_WITH_BITMAP);
+ RunPixelTestType(50, 2.f, PIXEL_TEST_SOFTWARE);
}
class ImageFilterClippedPixelTest : public LayerTreeHostFiltersPixelTest {
@@ -278,11 +270,11 @@
};
TEST_F(ImageFilterClippedPixelTest, ImageFilterClipped_GL) {
- RunPixelTestType(GL_WITH_BITMAP);
+ RunPixelTestType(PIXEL_TEST_GL);
}
TEST_F(ImageFilterClippedPixelTest, ImageFilterClipped_Software) {
- RunPixelTestType(SOFTWARE_WITH_BITMAP);
+ RunPixelTestType(PIXEL_TEST_SOFTWARE);
}
} // namespace
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index 5aef5e3..1b60eae 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -67,7 +67,7 @@
mask->SetIsMask(true);
green->SetMaskLayer(mask.get());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("mask_of_layer.png")));
}
@@ -98,7 +98,7 @@
green->SetMaskLayer(mask.get());
background->AddChild(green);
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("image_mask_of_layer.png")));
}
@@ -126,7 +126,7 @@
mask->SetIsMask(true);
green->SetMaskLayer(mask.get());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("mask_of_clipped_layer.png")));
}
@@ -156,7 +156,7 @@
replica->SetTransform(replica_transform);
green->SetReplicaLayer(replica.get());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("mask_with_replica.png")));
}
@@ -194,7 +194,7 @@
replica->SetTransform(replica_transform);
green->SetReplicaLayer(replica.get());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL(
"mask_with_replica_of_clipped_layer.png")));
@@ -230,7 +230,7 @@
replica->SetMaskLayer(mask.get());
green->SetReplicaLayer(replica.get());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
base::FilePath(FILE_PATH_LITERAL("mask_of_replica.png")));
}
@@ -272,10 +272,10 @@
replica->SetMaskLayer(mask.get());
green->SetReplicaLayer(replica.get());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
background,
- base::FilePath(FILE_PATH_LITERAL(
- "mask_of_replica_of_clipped_layer.png")));
+ base::FilePath(
+ FILE_PATH_LITERAL("mask_of_replica_of_clipped_layer.png")));
}
} // namespace
diff --git a/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc b/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
index 0689a27..74e20f5 100644
--- a/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
+++ b/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
@@ -11,8 +11,8 @@
#include "cc/trees/layer_tree_impl.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/rect_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
#if !defined(OS_ANDROID)
@@ -97,7 +97,7 @@
layer->SetBounds(layer_rect.size());
layer->SetPosition(layer_rect.origin());
- RunPixelTest(GL_WITH_BITMAP,
+ RunPixelTest(PIXEL_TEST_GL,
layer,
base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")));
}
diff --git a/cc/trees/layer_tree_host_pixeltest_readback.cc b/cc/trees/layer_tree_host_pixeltest_readback.cc
index 6b67e50..0a06144 100644
--- a/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -22,28 +22,51 @@
class LayerTreeHostReadbackPixelTest : public LayerTreePixelTest {
protected:
LayerTreeHostReadbackPixelTest()
- : insert_copy_request_after_frame_count_(0) {}
+ : readback_type_(READBACK_INVALID),
+ insert_copy_request_after_frame_count_(0) {}
+
+ enum ReadbackType {
+ READBACK_INVALID,
+ READBACK_DEFAULT,
+ READBACK_BITMAP,
+ };
+
+ void RunReadbackTest(PixelTestType test_type,
+ ReadbackType readback_type,
+ scoped_refptr<Layer> content_root,
+ base::FilePath file_name) {
+ readback_type_ = readback_type;
+ RunPixelTest(test_type, content_root, file_name);
+ }
+
+ void RunReadbackTestWithReadbackTarget(PixelTestType type,
+ ReadbackType readback_type,
+ scoped_refptr<Layer> content_root,
+ Layer* target,
+ base::FilePath file_name) {
+ readback_type_ = readback_type;
+ RunPixelTestWithReadbackTarget(type, content_root, target, file_name);
+ }
scoped_ptr<CopyOutputRequest> CreateCopyOutputRequest() override {
scoped_ptr<CopyOutputRequest> request;
- switch (test_type_) {
- case GL_WITH_BITMAP:
- case SOFTWARE_WITH_BITMAP:
- request = CopyOutputRequest::CreateBitmapRequest(
- base::Bind(&LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
- base::Unretained(this)));
- break;
- case SOFTWARE_WITH_DEFAULT:
+ if (readback_type_ == READBACK_BITMAP) {
+ request = CopyOutputRequest::CreateBitmapRequest(
+ base::Bind(&LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
+ base::Unretained(this)));
+ } else {
+ DCHECK_EQ(readback_type_, READBACK_DEFAULT);
+ if (test_type_ == PIXEL_TEST_SOFTWARE) {
request = CopyOutputRequest::CreateRequest(
base::Bind(&LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
base::Unretained(this)));
- break;
- case GL_WITH_DEFAULT:
+ } else {
+ DCHECK_EQ(test_type_, PIXEL_TEST_GL);
request = CopyOutputRequest::CreateRequest(
base::Bind(&LayerTreeHostReadbackPixelTest::ReadbackResultAsTexture,
base::Unretained(this)));
- break;
+ }
}
if (!copy_subrect_.IsEmpty())
@@ -93,421 +116,438 @@
ReadbackResultAsBitmap(CopyOutputResult::CreateBitmapResult(bitmap.Pass()));
}
+ ReadbackType readback_type_;
gfx::Rect copy_subrect_;
int insert_copy_request_after_frame_count_;
};
-void IgnoreReadbackResult(scoped_ptr<CopyOutputResult> result) {}
+void IgnoreReadbackResult(scoped_ptr<CopyOutputResult> result) {
+}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayer_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTest(SOFTWARE_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTest(PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayer_Software_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTest(SOFTWARE_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTest(PIXEL_TEST_SOFTWARE,
+ READBACK_BITMAP,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayer_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTest(GL_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTest(PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayer_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTest(GL_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTest(PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
-TEST_F(LayerTreeHostReadbackPixelTest,
- ReadbackRootLayerWithChild_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayerWithChild_Software) {
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTest(SOFTWARE_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayerWithChild_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTest(GL_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackRootLayerWithChild_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTest(GL_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayer_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayer_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayer_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green.png")));
}
-TEST_F(LayerTreeHostReadbackPixelTest,
- ReadbackSmallNonRootLayer_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayer_Software) {
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayer_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayer_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
background->AddChild(green);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSmallNonRootLayerWithChild_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSmallNonRootLayerWithChild_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayerWithChild_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSubtreeSurroundsTargetLayer_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorRED);
+ scoped_refptr<SolidColorLayer> target =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorRED);
background->AddChild(target);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
target->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
target->AddChild(blue);
copy_subrect_ = gfx::Rect(0, 0, 100, 100);
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- target.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSubtreeSurroundsLayer_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorRED);
+ scoped_refptr<SolidColorLayer> target =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorRED);
background->AddChild(target);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
target->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
target->AddChild(blue);
copy_subrect_ = gfx::Rect(0, 0, 100, 100);
- RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
- background,
- target.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
-TEST_F(LayerTreeHostReadbackPixelTest,
- ReadbackSubtreeSurroundsTargetLayer_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSubtreeSurroundsTargetLayer_GL) {
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
- gfx::Rect(100, 100, 100, 100), SK_ColorRED);
+ scoped_refptr<SolidColorLayer> target =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 100, 100), SK_ColorRED);
background->AddChild(target);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
target->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
target->AddChild(blue);
copy_subrect_ = gfx::Rect(0, 0, 100, 100);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- target.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSubtreeExtendsBeyondTargetLayer_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
- gfx::Rect(50, 50, 150, 150), SK_ColorRED);
+ scoped_refptr<SolidColorLayer> target =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 150, 150), SK_ColorRED);
background->AddChild(target);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
target->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
target->AddChild(blue);
copy_subrect_ = gfx::Rect(50, 50, 100, 100);
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- target.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSubtreeExtendsBeyondTargetLayer_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
- gfx::Rect(50, 50, 150, 150), SK_ColorRED);
+ scoped_refptr<SolidColorLayer> target =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 150, 150), SK_ColorRED);
background->AddChild(target);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
target->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
target->AddChild(blue);
copy_subrect_ = gfx::Rect(50, 50, 100, 100);
- RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
- background,
- target.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
ReadbackSubtreeExtendsBeyondTargetLayer_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
- gfx::Rect(50, 50, 150, 150), SK_ColorRED);
+ scoped_refptr<SolidColorLayer> target =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 150, 150), SK_ColorRED);
background->AddChild(target);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
target->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
target->AddChild(blue);
copy_subrect_ = gfx::Rect(50, 50, 100, 100);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- target.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackHiddenSubtree_Software) {
@@ -523,8 +563,9 @@
CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
hidden_target->AddChild(blue);
- RunPixelTestWithReadbackTarget(
- SOFTWARE_WITH_DEFAULT,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
background,
hidden_target.get(),
base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
@@ -543,8 +584,9 @@
CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
hidden_target->AddChild(blue);
- RunPixelTestWithReadbackTarget(
- GL_WITH_BITMAP,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
background,
hidden_target.get(),
base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
@@ -563,8 +605,9 @@
CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
hidden_target->AddChild(blue);
- RunPixelTestWithReadbackTarget(
- GL_WITH_DEFAULT,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
background,
hidden_target.get(),
base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
@@ -586,9 +629,10 @@
hidden_target->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
base::Bind(&IgnoreReadbackResult)));
- RunPixelTest(SOFTWARE_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL("black.png")));
+ RunReadbackTest(PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("black.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
@@ -607,9 +651,10 @@
hidden_target->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
base::Bind(&IgnoreReadbackResult)));
- RunPixelTest(GL_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL("black.png")));
+ RunReadbackTest(PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("black.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest,
@@ -628,138 +673,145 @@
hidden_target->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
base::Bind(&IgnoreReadbackResult)));
- RunPixelTest(GL_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL("black.png")));
+ RunReadbackTest(PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("black.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSubrect_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
// Grab the middle of the root layer.
copy_subrect_ = gfx::Rect(50, 50, 100, 100);
- RunPixelTest(SOFTWARE_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSubrect_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
// Grab the middle of the root layer.
copy_subrect_ = gfx::Rect(50, 50, 100, 100);
- RunPixelTest(GL_WITH_BITMAP,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSubrect_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
// Grab the middle of the root layer.
copy_subrect_ = gfx::Rect(50, 50, 100, 100);
- RunPixelTest(GL_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayerSubrect_Software) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(25, 25, 150, 150), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(25, 25, 150, 150), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(75, 75, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(75, 75, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
// Grab the middle of the green layer.
copy_subrect_ = gfx::Rect(25, 25, 100, 100);
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayerSubrect_GL_Bitmap) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(25, 25, 150, 150), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(25, 25, 150, 150), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(75, 75, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(75, 75, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
// Grab the middle of the green layer.
copy_subrect_ = gfx::Rect(25, 25, 100, 100);
- RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayerSubrect_GL) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(25, 25, 150, 150), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(25, 25, 150, 150), SK_ColorGREEN);
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(75, 75, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(75, 75, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
// Grab the middle of the green layer.
copy_subrect_ = gfx::Rect(25, 25, 100, 100);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackWhenNoDamage_Software) {
@@ -779,8 +831,9 @@
target->AddChild(blue);
insert_copy_request_after_frame_count_ = 1;
- RunPixelTestWithReadbackTarget(
- SOFTWARE_WITH_DEFAULT,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
background,
target.get(),
base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
@@ -803,8 +856,9 @@
target->AddChild(blue);
insert_copy_request_after_frame_count_ = 1;
- RunPixelTestWithReadbackTarget(
- GL_WITH_BITMAP,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
background,
target.get(),
base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
@@ -827,8 +881,9 @@
target->AddChild(blue);
insert_copy_request_after_frame_count_ = 1;
- RunPixelTestWithReadbackTarget(
- GL_WITH_DEFAULT,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
background,
target.get(),
base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
@@ -853,8 +908,9 @@
target->AddChild(blue);
insert_copy_request_after_frame_count_ = 1;
- RunPixelTestWithReadbackTarget(
- SOFTWARE_WITH_DEFAULT,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
background,
target.get(),
base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
@@ -879,8 +935,9 @@
target->AddChild(blue);
insert_copy_request_after_frame_count_ = 1;
- RunPixelTestWithReadbackTarget(
- GL_WITH_BITMAP,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_BITMAP,
background,
target.get(),
base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
@@ -904,8 +961,9 @@
target->AddChild(blue);
insert_copy_request_after_frame_count_ = 1;
- RunPixelTestWithReadbackTarget(
- GL_WITH_DEFAULT,
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
background,
target.get(),
base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
@@ -941,8 +999,7 @@
SolidColorContentLayerClient blue_client_;
};
-TEST_F(LayerTreeHostReadbackDeviceScalePixelTest,
- ReadbackSubrect_Software) {
+TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackSubrect_Software) {
scoped_refptr<FakePictureLayer> background =
FakePictureLayer::Create(&white_client_);
background->SetBounds(gfx::Size(100, 100));
@@ -964,14 +1021,14 @@
// Grab the middle of the root layer.
copy_subrect_ = gfx::Rect(25, 25, 50, 50);
device_scale_factor_ = 2.f;
- RunPixelTest(SOFTWARE_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
-TEST_F(LayerTreeHostReadbackDeviceScalePixelTest,
- ReadbackSubrect_GL) {
+TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackSubrect_GL) {
scoped_refptr<FakePictureLayer> background =
FakePictureLayer::Create(&white_client_);
background->SetBounds(gfx::Size(100, 100));
@@ -993,10 +1050,11 @@
// Grab the middle of the root layer.
copy_subrect_ = gfx::Rect(25, 25, 50, 50);
device_scale_factor_ = 2.f;
- RunPixelTest(GL_WITH_DEFAULT,
- background,
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTest(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackDeviceScalePixelTest,
@@ -1023,11 +1081,12 @@
// Grab the green layer's content with blue in the bottom right.
copy_subrect_ = gfx::Rect(25, 25, 50, 50);
device_scale_factor_ = 2.f;
- RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_SOFTWARE,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackDeviceScalePixelTest,
@@ -1054,33 +1113,35 @@
// Grab the green layer's content with blue in the bottom right.
copy_subrect_ = gfx::Rect(25, 25, 50, 50);
device_scale_factor_ = 2.f;
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_small_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png")));
}
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayerOutsideViewport) {
- scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorWHITE);
+ scoped_refptr<SolidColorLayer> background =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
- scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
- gfx::Rect(200, 200), SK_ColorGREEN);
+ scoped_refptr<SolidColorLayer> green =
+ CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
// Only the top left quarter of the layer is inside the viewport, so the
// blue layer is entirely outside.
green->SetPosition(gfx::Point(100, 100));
background->AddChild(green);
- scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
- gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
+ scoped_refptr<SolidColorLayer> blue =
+ CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
green->AddChild(blue);
- RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
- background,
- green.get(),
- base::FilePath(FILE_PATH_LITERAL(
- "green_with_blue_corner.png")));
+ RunReadbackTestWithReadbackTarget(
+ PIXEL_TEST_GL,
+ READBACK_DEFAULT,
+ background,
+ green.get(),
+ base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")));
}
} // namespace
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index cb3cdef..d15c6a3 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -53,9 +53,9 @@
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "ui/gfx/frame_time.h"
-#include "ui/gfx/point_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/vector2d_conversions.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
using testing::_;
using testing::AnyNumber;
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index c4f2eb3..e922f21 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -16,9 +16,9 @@
#include "cc/test/layer_tree_test.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/trees/layer_tree_impl.h"
-#include "ui/gfx/point_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/vector2d_conversions.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
namespace cc {
namespace {
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 206ec60..8f2f013 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -27,9 +27,9 @@
#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/occlusion_tracker.h"
-#include "ui/gfx/point_conversions.h"
-#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/vector2d_conversions.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
namespace cc {
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 7fc1645..f8de8b0 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -13,7 +13,7 @@
#include "cc/test/layer_tree_host_common_test.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/trees/layer_tree_host_impl.h"
-#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
namespace {
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index e4d5f1d..1d5cf1b 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -67,6 +67,7 @@
ignore_root_layer_flings(false),
use_rgba_4444_textures(false),
texture_id_allocation_chunk_size(64),
+ scheduled_raster_task_limit(32),
use_occlusion_for_tile_prioritization(false),
record_full_layer(false) {
}
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index fff1084..74a3b0c 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -9,7 +9,7 @@
#include "cc/base/cc_export.h"
#include "cc/debug/layer_tree_debug_state.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/size.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
@@ -77,6 +77,7 @@
bool ignore_root_layer_flings;
bool use_rgba_4444_textures;
size_t texture_id_allocation_chunk_size;
+ size_t scheduled_raster_task_limit;
bool use_occlusion_for_tile_prioritization;
bool record_full_layer;
diff --git a/cc/trees/occlusion.cc b/cc/trees/occlusion.cc
index 70ee32c..14168f9 100644
--- a/cc/trees/occlusion.cc
+++ b/cc/trees/occlusion.cc
@@ -5,7 +5,7 @@
#include "cc/trees/occlusion.h"
#include "cc/base/math_util.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
diff --git a/cc/trees/occlusion.h b/cc/trees/occlusion.h
index 6d1e5b1..56f9962 100644
--- a/cc/trees/occlusion.h
+++ b/cc/trees/occlusion.h
@@ -8,7 +8,7 @@
#include "base/basictypes.h"
#include "cc/base/cc_export.h"
#include "cc/base/simple_enclosed_region.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/transform.h"
namespace cc {
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index 7d90834..bce1fd5 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -12,8 +12,8 @@
#include "cc/layers/layer_impl.h"
#include "cc/layers/render_surface.h"
#include "cc/layers/render_surface_impl.h"
-#include "ui/gfx/quad_f.h"
-#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/geometry/quad_f.h"
+#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
diff --git a/cc/trees/occlusion_tracker.h b/cc/trees/occlusion_tracker.h
index b96752c..4d6555a 100644
--- a/cc/trees/occlusion_tracker.h
+++ b/cc/trees/occlusion_tracker.h
@@ -12,7 +12,7 @@
#include "cc/base/simple_enclosed_region.h"
#include "cc/layers/layer_iterator.h"
#include "cc/trees/occlusion.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
class LayerImpl;
diff --git a/gin/array_buffer.h b/gin/array_buffer.h
index 048a35f..fe3fae2 100644
--- a/gin/array_buffer.h
+++ b/gin/array_buffer.h
@@ -16,9 +16,9 @@
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
- virtual void* Allocate(size_t length) override;
- virtual void* AllocateUninitialized(size_t length) override;
- virtual void Free(void* data, size_t length) override;
+ void* Allocate(size_t length) override;
+ void* AllocateUninitialized(size_t length) override;
+ void Free(void* data, size_t length) override;
GIN_EXPORT static ArrayBufferAllocator* SharedInstance();
};
diff --git a/gin/interceptor_unittest.cc b/gin/interceptor_unittest.cc
index d267100..4d2d7fb 100644
--- a/gin/interceptor_unittest.cc
+++ b/gin/interceptor_unittest.cc
@@ -31,9 +31,8 @@
void set_value(int value) { value_ = value; }
// gin::NamedPropertyInterceptor
- virtual v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate,
- const std::string& property)
- override {
+ v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate,
+ const std::string& property) override {
if (property == "value") {
return ConvertToV8(isolate, value_);
} else if (property == "func") {
@@ -42,16 +41,16 @@
return v8::Local<v8::Value>();
}
}
- virtual bool SetNamedProperty(v8::Isolate* isolate,
- const std::string& property,
- v8::Local<v8::Value> value) override {
+ bool SetNamedProperty(v8::Isolate* isolate,
+ const std::string& property,
+ v8::Local<v8::Value> value) override {
if (property == "value") {
ConvertFromV8(isolate, value, &value_);
return true;
}
return false;
}
- virtual std::vector<std::string> EnumerateNamedProperties(
+ std::vector<std::string> EnumerateNamedProperties(
v8::Isolate* isolate) override {
std::vector<std::string> result;
result.push_back("func");
@@ -60,15 +59,15 @@
}
// gin::IndexedPropertyInterceptor
- virtual v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate,
- uint32_t index) override {
+ v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate,
+ uint32_t index) override {
if (index == 0)
return ConvertToV8(isolate, value_);
return v8::Local<v8::Value>();
}
- virtual bool SetIndexedProperty(v8::Isolate* isolate,
- uint32_t index,
- v8::Local<v8::Value> value) override {
+ bool SetIndexedProperty(v8::Isolate* isolate,
+ uint32_t index,
+ v8::Local<v8::Value> value) override {
if (index == 0) {
ConvertFromV8(isolate, value, &value_);
return true;
@@ -76,8 +75,8 @@
// Don't allow bypassing the interceptor.
return true;
}
- virtual std::vector<uint32_t> EnumerateIndexedProperties(v8::Isolate* isolate)
- override {
+ std::vector<uint32_t> EnumerateIndexedProperties(
+ v8::Isolate* isolate) override {
std::vector<uint32_t> result;
result.push_back(0);
return result;
@@ -89,11 +88,11 @@
IndexedPropertyInterceptor(isolate, this),
value_(0),
template_cache_(isolate) {}
- virtual ~MyInterceptor() {}
+ ~MyInterceptor() override {}
// gin::Wrappable
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate)
- override {
+ ObjectTemplateBuilder GetObjectTemplateBuilder(
+ v8::Isolate* isolate) override {
return Wrappable<MyInterceptor>::GetObjectTemplateBuilder(isolate)
.AddNamedPropertyInterceptor()
.AddIndexedPropertyInterceptor();
diff --git a/gin/modules/module_registry_unittest.cc b/gin/modules/module_registry_unittest.cc
index 57bc02d..3d784dc 100644
--- a/gin/modules/module_registry_unittest.cc
+++ b/gin/modules/module_registry_unittest.cc
@@ -35,7 +35,7 @@
public:
ModuleRegistryObserverImpl() : did_add_count_(0) {}
- virtual void OnDidAddPendingModule(
+ void OnDidAddPendingModule(
const std::string& id,
const std::vector<std::string>& dependencies) override {
did_add_count_++;
diff --git a/gin/modules/module_runner_delegate.h b/gin/modules/module_runner_delegate.h
index 421e23a..bd174f0 100644
--- a/gin/modules/module_runner_delegate.h
+++ b/gin/modules/module_runner_delegate.h
@@ -26,7 +26,7 @@
public:
explicit ModuleRunnerDelegate(
const std::vector<base::FilePath>& search_paths);
- virtual ~ModuleRunnerDelegate();
+ ~ModuleRunnerDelegate() override;
void AddBuiltinModule(const std::string& id, ModuleGetter getter);
void AddBuiltinModule(const std::string& id,
@@ -39,11 +39,11 @@
typedef std::map<std::string, ModuleGetterCallback> BuiltinModuleMap;
// From ShellRunnerDelegate:
- virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
+ v8::Handle<v8::ObjectTemplate> GetGlobalTemplate(
ShellRunner* runner,
v8::Isolate* isolate) override;
- virtual void DidCreateContext(ShellRunner* runner) override;
- virtual void DidRunScript(ShellRunner* runner) override;
+ void DidCreateContext(ShellRunner* runner) override;
+ void DidRunScript(ShellRunner* runner) override;
BuiltinModuleMap builtin_modules_;
FileModuleProvider module_provider_;
diff --git a/gin/modules/timer.h b/gin/modules/timer.h
index 6284668..4913477 100644
--- a/gin/modules/timer.h
+++ b/gin/modules/timer.h
@@ -29,13 +29,12 @@
static Handle<Timer> Create(TimerType type, v8::Isolate* isolate,
int delay_ms, v8::Handle<v8::Function> function);
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
- v8::Isolate* isolate) override;
+ ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override;
private:
Timer(v8::Isolate* isolate, bool repeating, int delay_ms,
v8::Handle<v8::Function> function);
- virtual ~Timer();
+ ~Timer() override;
void OnTimerFired();
base::Timer timer_;
@@ -55,10 +54,9 @@
private:
TimerModule();
- virtual ~TimerModule();
+ ~TimerModule() override;
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
- v8::Isolate* isolate) override;
+ ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override;
};
} // namespace gin
diff --git a/gin/modules/timer_unittest.cc b/gin/modules/timer_unittest.cc
index 42d7050..705bdc5 100644
--- a/gin/modules/timer_unittest.cc
+++ b/gin/modules/timer_unittest.cc
@@ -37,10 +37,9 @@
Result() : count_(0) {
}
- virtual ~Result() {
- }
+ ~Result() override {}
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
+ ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return Wrappable<Result>::GetObjectTemplateBuilder(isolate)
.SetProperty("count", &Result::count, &Result::set_count)
diff --git a/gin/per_context_data.h b/gin/per_context_data.h
index 0d11653..de8f179 100644
--- a/gin/per_context_data.h
+++ b/gin/per_context_data.h
@@ -24,7 +24,7 @@
public:
PerContextData(ContextHolder* context_holder,
v8::Handle<v8::Context> context);
- virtual ~PerContextData();
+ ~PerContextData() override;
// Can return NULL after the ContextHolder has detached from context.
static PerContextData* From(v8::Handle<v8::Context> context);
diff --git a/gin/public/v8_platform.h b/gin/public/v8_platform.h
index 59e2147..a4fc28a 100644
--- a/gin/public/v8_platform.h
+++ b/gin/public/v8_platform.h
@@ -19,17 +19,17 @@
static V8Platform* Get();
// v8::Platform implementation.
- virtual void CallOnBackgroundThread(
+ void CallOnBackgroundThread(
v8::Task* task,
v8::Platform::ExpectedRuntime expected_runtime) override;
- virtual void CallOnForegroundThread(v8::Isolate* isolate,
- v8::Task* task) override;
- virtual double MonotonicallyIncreasingTime() override;
+ void CallOnForegroundThread(v8::Isolate* isolate, v8::Task* task) override;
+ double MonotonicallyIncreasingTime() override;
+
private:
friend struct base::DefaultLazyInstanceTraits<V8Platform>;
V8Platform();
- virtual ~V8Platform();
+ ~V8Platform() override;
DISALLOW_COPY_AND_ASSIGN(V8Platform);
};
diff --git a/gin/run_microtasks_observer.h b/gin/run_microtasks_observer.h
index d31d804..7f1431f 100644
--- a/gin/run_microtasks_observer.h
+++ b/gin/run_microtasks_observer.h
@@ -18,8 +18,8 @@
public:
RunMicrotasksObserver(v8::Isolate* isolate);
- virtual void WillProcessTask(const base::PendingTask& pending_task) override;
- virtual void DidProcessTask(const base::PendingTask& pending_task) override;
+ void WillProcessTask(const base::PendingTask& pending_task) override;
+ void DidProcessTask(const base::PendingTask& pending_task) override;
private:
v8::Isolate* isolate_;
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc
index 77b167a..b17ec0a 100644
--- a/gin/shell/gin_main.cc
+++ b/gin/shell/gin_main.cc
@@ -44,8 +44,7 @@
AddBuiltinModule(Console::kModuleName, Console::GetModule);
}
- virtual void UnhandledException(ShellRunner* runner,
- TryCatch& try_catch) override {
+ void UnhandledException(ShellRunner* runner, TryCatch& try_catch) override {
ModuleRunnerDelegate::UnhandledException(runner, try_catch);
LOG(ERROR) << try_catch.GetStackTrace();
}
diff --git a/gin/shell_runner.h b/gin/shell_runner.h
index ca88a5d..9a5b4ef 100644
--- a/gin/shell_runner.h
+++ b/gin/shell_runner.h
@@ -37,19 +37,19 @@
class GIN_EXPORT ShellRunner : public Runner {
public:
ShellRunner(ShellRunnerDelegate* delegate, v8::Isolate* isolate);
- virtual ~ShellRunner();
+ ~ShellRunner() override;
// Before running script in this context, you'll need to enter the runner's
// context by creating an instance of Runner::Scope on the stack.
// Runner overrides:
- virtual void Run(const std::string& source,
- const std::string& resource_name) override;
- virtual v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function,
- v8::Handle<v8::Value> receiver,
- int argc,
- v8::Handle<v8::Value> argv[]) override;
- virtual ContextHolder* GetContextHolder() override;
+ void Run(const std::string& source,
+ const std::string& resource_name) override;
+ v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function,
+ v8::Handle<v8::Value> receiver,
+ int argc,
+ v8::Handle<v8::Value> argv[]) override;
+ ContextHolder* GetContextHolder() override;
private:
friend class Scope;
diff --git a/gin/test/file_runner.h b/gin/test/file_runner.h
index 5082cd5..9f7ab4b 100644
--- a/gin/test/file_runner.h
+++ b/gin/test/file_runner.h
@@ -20,12 +20,11 @@
class FileRunnerDelegate : public ModuleRunnerDelegate {
public:
FileRunnerDelegate();
- virtual ~FileRunnerDelegate();
+ ~FileRunnerDelegate() override;
private:
// From ModuleRunnerDelegate:
- virtual void UnhandledException(ShellRunner* runner,
- TryCatch& try_catch) override;
+ void UnhandledException(ShellRunner* runner, TryCatch& try_catch) override;
DISALLOW_COPY_AND_ASSIGN(FileRunnerDelegate);
};
diff --git a/gin/wrappable_unittest.cc b/gin/wrappable_unittest.cc
index 0e10c32..8dbaf30 100644
--- a/gin/wrappable_unittest.cc
+++ b/gin/wrappable_unittest.cc
@@ -40,9 +40,8 @@
protected:
MyObject() : value_(0) {}
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
- v8::Isolate* isolate) override;
- virtual ~MyObject() {}
+ ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) override;
+ ~MyObject() override {}
private:
int value_;
@@ -61,7 +60,7 @@
std::string result;
private:
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
+ ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return MyObject::GetObjectTemplateBuilder(isolate)
.SetMethod("sayHello", &MyObjectSubclass::SayHello);
@@ -70,8 +69,7 @@
MyObjectSubclass() {
}
- virtual ~MyObjectSubclass() {
- }
+ ~MyObjectSubclass() override {}
};
class MyCallableObject : public Wrappable<MyCallableObject> {
@@ -85,7 +83,7 @@
int result() { return result_; }
private:
- virtual ObjectTemplateBuilder GetObjectTemplateBuilder(
+ ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return Wrappable<MyCallableObject>::GetObjectTemplateBuilder(isolate)
.SetCallAsFunctionHandler(&MyCallableObject::Call);
@@ -94,8 +92,7 @@
MyCallableObject() : result_(0) {
}
- virtual ~MyCallableObject() {
- }
+ ~MyCallableObject() override {}
void Call(int val, const gin::Arguments& arguments) {
if (arguments.IsConstructCall())
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index b77dfca..d1df1dd 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -25,7 +25,6 @@
#
# gpu.gyp:gpu_config => //gpu/config
#
-# gpu.gyp:gpu_ipc => //gpu/ipc
#
# gpu.gyp:disk_cache_proto => //gpu/command_buffer/service:disk_cache_proto
#
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index 843cb31..c6a3919 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -237,5 +237,6 @@
GLES2_GET_FUN(ScheduleOverlayPlaneCHROMIUM)
#define glMatrixLoadfCHROMIUM GLES2_GET_FUN(MatrixLoadfCHROMIUM)
#define glMatrixLoadIdentityCHROMIUM GLES2_GET_FUN(MatrixLoadIdentityCHROMIUM)
+#define glBlendBarrierKHR GLES2_GET_FUN(BlendBarrierKHR)
#endif // GPU_GLES2_GL2CHROMIUM_AUTOGEN_H_
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 14c778a..94c3b34 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1546,6 +1546,12 @@
'type': 'StateSet',
'state': 'BlendFunc',
},
+ 'BlendBarrierKHR': {
+ 'gl_test_func': 'glBlendBarrierKHR',
+ 'extension': True,
+ 'extension_flag': 'blend_equation_advanced',
+ 'client_test': False,
+ },
'SampleCoverage': {'decoder_func': 'DoSampleCoverage'},
'StencilFunc': {
'type': 'StateSetFrontBack',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 71e3c82..392bb22 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -958,6 +958,9 @@
void GLES2MatrixLoadIdentityCHROMIUM(GLenum matrixMode) {
gles2::GetGLContext()->MatrixLoadIdentityCHROMIUM(matrixMode);
}
+void GLES2BlendBarrierKHR() {
+ gles2::GetGLContext()->BlendBarrierKHR();
+}
namespace gles2 {
@@ -1817,6 +1820,10 @@
reinterpret_cast<GLES2FunctionPointer>(glMatrixLoadIdentityCHROMIUM),
},
{
+ "glBlendBarrierKHR",
+ reinterpret_cast<GLES2FunctionPointer>(glBlendBarrierKHR),
+ },
+ {
NULL,
NULL,
},
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index c8432da..24d8be2 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -1929,4 +1929,11 @@
}
}
+void BlendBarrierKHR() {
+ gles2::cmds::BlendBarrierKHR* c = GetCmdSpace<gles2::cmds::BlendBarrierKHR>();
+ if (c) {
+ c->Init();
+ }
+}
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_CMD_HELPER_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 5177941..3ef98dd 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -691,4 +691,6 @@
void MatrixLoadIdentityCHROMIUM(GLenum matrixMode) override;
+virtual void BlendBarrierKHR() override;
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
index 50d7f82..32b1347 100644
--- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -2162,4 +2162,12 @@
CheckGLError();
}
+void GLES2Implementation::BlendBarrierKHR() {
+ GPU_CLIENT_SINGLE_THREAD_CHECK();
+ GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glBlendBarrierKHR("
+ << ")");
+ helper_->BlendBarrierKHR();
+ CheckGLError();
+}
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_IMPLEMENTATION_IMPL_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 595bde1..d840804 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -495,4 +495,5 @@
GLfloat uv_height) = 0;
virtual void MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) = 0;
virtual void MatrixLoadIdentityCHROMIUM(GLenum matrixMode) = 0;
+virtual void BlendBarrierKHR() = 0;
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_INTERFACE_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index bd1a73e..687ff48 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -486,4 +486,5 @@
GLfloat uv_height) override;
void MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) override;
void MatrixLoadIdentityCHROMIUM(GLenum matrixMode) override;
+void BlendBarrierKHR() override;
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_INTERFACE_STUB_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index 0936086..e78b07c 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -851,4 +851,6 @@
}
void GLES2InterfaceStub::MatrixLoadIdentityCHROMIUM(GLenum /* matrixMode */) {
}
+void GLES2InterfaceStub::BlendBarrierKHR() {
+}
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_INTERFACE_STUB_IMPL_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 22c12bc..a599eb4 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -486,4 +486,5 @@
GLfloat uv_height) override;
void MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) override;
void MatrixLoadIdentityCHROMIUM(GLenum matrixMode) override;
+void BlendBarrierKHR() override;
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_TRACE_IMPLEMENTATION_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index d1b5fce..3173a25 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -1509,4 +1509,9 @@
gl_->MatrixLoadIdentityCHROMIUM(matrixMode);
}
+void GLES2TraceImplementation::BlendBarrierKHR() {
+ TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::BlendBarrierKHR");
+ gl_->BlendBarrierKHR();
+}
+
#endif // GPU_COMMAND_BUFFER_CLIENT_GLES2_TRACE_IMPLEMENTATION_IMPL_AUTOGEN_H_
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt
index 49cb433..37c33be 100644
--- a/gpu/command_buffer/cmd_buffer_functions.txt
+++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -222,3 +222,5 @@
GL_APICALL void GL_APIENTRY glMatrixLoadfCHROMIUM (GLenumMatrixMode matrixMode, const GLfloat* m);
GL_APICALL void GL_APIENTRY glMatrixLoadIdentityCHROMIUM (GLenumMatrixMode matrixMode);
+// Extension KHR_blend_equation_advanced
+GL_APICALL void GL_APIENTRY glBlendBarrierKHR (void);
diff --git a/gpu/command_buffer/command_buffer_nacl.gyp b/gpu/command_buffer/command_buffer_nacl.gyp
index aabd1c8..319fc67 100644
--- a/gpu/command_buffer/command_buffer_nacl.gyp
+++ b/gpu/command_buffer/command_buffer_nacl.gyp
@@ -23,10 +23,13 @@
'build_glibc': 0,
'build_newlib': 0,
'build_irt': 1,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
},
'dependencies': [
'../../native_client/tools.gyp:prep_toolchain',
'../../base/base_nacl.gyp:base_nacl',
+ '../../base/base_nacl.gyp:base_nacl_nonsfi',
'../../third_party/khronos/khronos.gyp:khronos_headers',
],
},
diff --git a/gpu/command_buffer/common/capabilities.cc b/gpu/command_buffer/common/capabilities.cc
index f51071a..eacf10c 100644
--- a/gpu/command_buffer/common/capabilities.cc
+++ b/gpu/command_buffer/common/capabilities.cc
@@ -20,7 +20,9 @@
sync_query(false),
image(false),
future_sync_points(false),
- blend_minmax(false) {
+ blend_minmax(false),
+ blend_equation_advanced(false),
+ blend_equation_advanced_coherent(false) {
}
} // namespace gpu
diff --git a/gpu/command_buffer/common/capabilities.h b/gpu/command_buffer/common/capabilities.h
index df97d1f..9de6023 100644
--- a/gpu/command_buffer/common/capabilities.h
+++ b/gpu/command_buffer/common/capabilities.h
@@ -24,6 +24,8 @@
bool image;
bool future_sync_points;
bool blend_minmax;
+ bool blend_equation_advanced;
+ bool blend_equation_advanced_coherent;
Capabilities();
};
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index ccdc040..487e8b7 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -9014,4 +9014,30 @@
COMPILE_ASSERT(offsetof(MatrixLoadIdentityCHROMIUM, matrixMode) == 4,
OffsetOf_MatrixLoadIdentityCHROMIUM_matrixMode_not_4);
+struct BlendBarrierKHR {
+ typedef BlendBarrierKHR ValueType;
+ static const CommandId kCmdId = kBlendBarrierKHR;
+ static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+ static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+ static uint32_t ComputeSize() {
+ return static_cast<uint32_t>(sizeof(ValueType)); // NOLINT
+ }
+
+ void SetHeader() { header.SetCmd<ValueType>(); }
+
+ void Init() { SetHeader(); }
+
+ void* Set(void* cmd) {
+ static_cast<ValueType*>(cmd)->Init();
+ return NextCmdAddress<ValueType>(cmd);
+ }
+
+ gpu::CommandHeader header;
+};
+
+COMPILE_ASSERT(sizeof(BlendBarrierKHR) == 4, Sizeof_BlendBarrierKHR_is_not_4);
+COMPILE_ASSERT(offsetof(BlendBarrierKHR, header) == 0,
+ OffsetOf_BlendBarrierKHR_header_not_0);
+
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index 7b7845b..b26afb1 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -3432,4 +3432,13 @@
CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
}
+TEST_F(GLES2FormatTest, BlendBarrierKHR) {
+ cmds::BlendBarrierKHR& cmd = *GetBufferAs<cmds::BlendBarrierKHR>();
+ void* next_cmd = cmd.Set(&cmd);
+ EXPECT_EQ(static_cast<uint32_t>(cmds::BlendBarrierKHR::kCmdId),
+ cmd.header.command);
+ EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+ CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
#endif // GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_TEST_AUTOGEN_H_
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index 030ada2..8bd5c0a 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -209,7 +209,8 @@
OP(DiscardBackbufferCHROMIUM) /* 450 */ \
OP(ScheduleOverlayPlaneCHROMIUM) /* 451 */ \
OP(MatrixLoadfCHROMIUMImmediate) /* 452 */ \
- OP(MatrixLoadIdentityCHROMIUM) /* 453 */
+ OP(MatrixLoadIdentityCHROMIUM) /* 453 */ \
+ OP(BlendBarrierKHR) /* 454 */
enum CommandId {
kStartPoint = cmd::kLastCommonId, // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index bda7a82..a21f630 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -144,7 +144,9 @@
angle_texture_usage(false),
ext_texture_storage(false),
chromium_path_rendering(false),
- ext_blend_minmax(false) {
+ ext_blend_minmax(false),
+ blend_equation_advanced(false),
+ blend_equation_advanced_coherent(false) {
}
FeatureInfo::Workarounds::Workarounds() :
@@ -880,6 +882,40 @@
feature_flags_.chromium_sync_query = true;
}
+ bool blend_equation_advanced_coherent =
+ extensions.Contains("GL_NV_blend_equation_advanced_coherent") ||
+ extensions.Contains("GL_KHR_blend_equation_advanced_coherent");
+
+ if (blend_equation_advanced_coherent ||
+ extensions.Contains("GL_NV_blend_equation_advanced") ||
+ extensions.Contains("GL_KHR_blend_equation_advanced")) {
+ const GLenum equations[] = {GL_MULTIPLY_KHR,
+ GL_SCREEN_KHR,
+ GL_OVERLAY_KHR,
+ GL_DARKEN_KHR,
+ GL_LIGHTEN_KHR,
+ GL_COLORDODGE_KHR,
+ GL_COLORBURN_KHR,
+ GL_HARDLIGHT_KHR,
+ GL_SOFTLIGHT_KHR,
+ GL_DIFFERENCE_KHR,
+ GL_EXCLUSION_KHR,
+ GL_HSL_HUE_KHR,
+ GL_HSL_SATURATION_KHR,
+ GL_HSL_COLOR_KHR,
+ GL_HSL_LUMINOSITY_KHR};
+
+ for (GLenum equation : equations)
+ validators_.equation.AddValue(equation);
+ if (blend_equation_advanced_coherent)
+ AddExtensionString("GL_KHR_blend_equation_advanced_coherent");
+
+ AddExtensionString("GL_KHR_blend_equation_advanced");
+ feature_flags_.blend_equation_advanced = true;
+ feature_flags_.blend_equation_advanced_coherent =
+ blend_equation_advanced_coherent;
+ }
+
if (extensions.Contains("GL_NV_path_rendering")) {
if (extensions.Contains("GL_EXT_direct_state_access") || is_es3) {
AddExtensionString("GL_CHROMIUM_path_rendering");
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index a30581a..0bc00a0 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -72,6 +72,8 @@
bool ext_texture_storage;
bool chromium_path_rendering;
bool ext_blend_minmax;
+ bool blend_equation_advanced;
+ bool blend_equation_advanced_coherent;
};
struct Workarounds {
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index 004eeee..a97188f 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -1347,5 +1347,39 @@
Not(HasSubstr("GL_CHROMIUM_path_rendering")));
}
+TEST_F(FeatureInfoTest, InitializeNoKHR_blend_equation_advanced) {
+ SetupInitExpectationsWithGLVersion("", "", "4.3");
+ EXPECT_FALSE(info_->feature_flags().blend_equation_advanced);
+ EXPECT_THAT(info_->extensions(),
+ Not(HasSubstr("GL_KHR_blend_equation_advanced")));
+}
+
+TEST_F(FeatureInfoTest, InitializeKHR_blend_equations_advanced) {
+ SetupInitExpectations("GL_KHR_blend_equation_advanced");
+ EXPECT_THAT(info_->extensions(), HasSubstr("GL_KHR_blend_equation_advanced"));
+ EXPECT_TRUE(info_->feature_flags().blend_equation_advanced);
+}
+
+TEST_F(FeatureInfoTest, InitializeNV_blend_equations_advanced) {
+ SetupInitExpectations("GL_NV_blend_equation_advanced");
+ EXPECT_THAT(info_->extensions(), HasSubstr("GL_KHR_blend_equation_advanced"));
+ EXPECT_TRUE(info_->feature_flags().blend_equation_advanced);
+}
+
+TEST_F(FeatureInfoTest, InitializeNoKHR_blend_equation_advanced_coherent) {
+ SetupInitExpectationsWithGLVersion("", "", "4.3");
+ EXPECT_FALSE(info_->feature_flags().blend_equation_advanced_coherent);
+ EXPECT_THAT(info_->extensions(),
+ Not(HasSubstr("GL_KHR_blend_equation_advanced_coherent")));
+}
+
+TEST_F(FeatureInfoTest, InitializeKHR_blend_equations_advanced_coherent) {
+ SetupInitExpectations("GL_KHR_blend_equation_advanced_coherent");
+ EXPECT_THAT(info_->extensions(),
+ HasSubstr("GL_KHR_blend_equation_advanced_coherent"));
+ EXPECT_TRUE(info_->feature_flags().blend_equation_advanced);
+ EXPECT_TRUE(info_->feature_flags().blend_equation_advanced_coherent);
+}
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 0b8d68d..01d20c7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -2742,6 +2742,10 @@
caps.image = true;
caps.blend_minmax = feature_info_->feature_flags().ext_blend_minmax;
+ caps.blend_equation_advanced =
+ feature_info_->feature_flags().blend_equation_advanced;
+ caps.blend_equation_advanced_coherent =
+ feature_info_->feature_flags().blend_equation_advanced_coherent;
return caps;
}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index dade363..02d914f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -3302,6 +3302,22 @@
return error::kNoError;
}
+error::Error GLES2DecoderImpl::HandleBlendBarrierKHR(
+ uint32_t immediate_data_size,
+ const void* cmd_data) {
+ const gles2::cmds::BlendBarrierKHR& c =
+ *static_cast<const gles2::cmds::BlendBarrierKHR*>(cmd_data);
+ (void)c;
+ if (!features().blend_equation_advanced) {
+ LOCAL_SET_GL_ERROR(
+ GL_INVALID_OPERATION, "glBlendBarrierKHR", "function not available");
+ return error::kNoError;
+ }
+
+ glBlendBarrierKHR();
+ return error::kNoError;
+}
+
bool GLES2DecoderImpl::SetCapabilityState(GLenum cap, bool enabled) {
switch (cap) {
case GL_BLEND:
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
index 311e798..ac80190 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
@@ -35,6 +35,27 @@
INSTANTIATE_TEST_CASE_P(Service,
GLES2DecoderTestWithCHROMIUMPathRendering,
::testing::Bool());
+
+class GLES2DecoderTestWithBlendEquationAdvanced : public GLES2DecoderTest {
+ public:
+ GLES2DecoderTestWithBlendEquationAdvanced() {}
+ virtual void SetUp() override {
+ InitState init;
+ init.gl_version = "opengl es 2.0";
+ init.has_alpha = true;
+ init.has_depth = true;
+ init.request_alpha = true;
+ init.request_depth = true;
+ init.bind_generates_resource = true;
+ init.extensions = "GL_KHR_blend_equation_advanced";
+ InitDecoder(init);
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(Service,
+ GLES2DecoderTestWithBlendEquationAdvanced,
+ ::testing::Bool());
+
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h"
} // namespace gles2
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
index a81be2f..57db672 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
@@ -44,4 +44,13 @@
EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
}
+
+TEST_P(GLES2DecoderTestWithBlendEquationAdvanced, BlendBarrierKHRValidArgs) {
+ EXPECT_CALL(*gl_, BlendBarrierKHR());
+ SpecializedSetup<cmds::BlendBarrierKHR, 0>(true);
+ cmds::BlendBarrierKHR cmd;
+ cmd.Init();
+ EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
#endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_EXTENSIONS_AUTOGEN_H_
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index c152baf..5968dab 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -29,6 +29,11 @@
'sources': [
'<@(gles2_implementation_source_files)',
],
+ 'includes': [
+ # Disable LTO due to ELF section name out of range
+ # crbug.com/422251
+ '../build/android/disable_lto.gypi',
+ ],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
},
@@ -479,6 +484,9 @@
'includes': [
'command_buffer_service.gypi',
'../build/android/increase_size_for_speed.gypi',
+ # Disable LTO due to ELF section name out of range
+ # crbug.com/422251
+ '../build/android/disable_lto.gypi',
],
'dependencies': [
'command_buffer_common',
diff --git a/gpu/gpu_nacl.gyp b/gpu/gpu_nacl.gyp
index 3d8a1c1..003f0af 100644
--- a/gpu/gpu_nacl.gyp
+++ b/gpu/gpu_nacl.gyp
@@ -24,6 +24,8 @@
'build_glibc': 0,
'build_newlib': 0,
'build_irt': 1,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
},
'defines': [
'GLES2_IMPL_IMPLEMENTATION',
@@ -32,8 +34,9 @@
'<@(gles2_implementation_source_files)',
],
'dependencies': [
- '../native_client/tools.gyp:prep_toolchain',
'../base/base_nacl.gyp:base_nacl',
+ '../base/base_nacl.gyp:base_nacl_nonsfi',
+ '../native_client/tools.gyp:prep_toolchain',
'../third_party/khronos/khronos.gyp:khronos_headers',
'command_buffer/command_buffer_nacl.gyp:gles2_utils_nacl',
'gles2_cmd_helper_nacl',
@@ -48,13 +51,16 @@
'build_glibc': 0,
'build_newlib': 0,
'build_irt': 1,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
},
'includes': [
'command_buffer_common.gypi',
],
'dependencies': [
- '../native_client/tools.gyp:prep_toolchain',
'../base/base_nacl.gyp:base_nacl',
+ '../base/base_nacl.gyp:base_nacl_nonsfi',
+ '../native_client/tools.gyp:prep_toolchain',
'command_buffer/command_buffer_nacl.gyp:gles2_utils_nacl',
],
},
@@ -67,13 +73,16 @@
'build_glibc': 0,
'build_newlib': 0,
'build_irt': 1,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
},
'includes': [
'gles2_cmd_helper.gypi',
],
'dependencies': [
- '../native_client/tools.gyp:prep_toolchain',
'../base/base_nacl.gyp:base_nacl',
+ '../base/base_nacl.gyp:base_nacl_nonsfi',
+ '../native_client/tools.gyp:prep_toolchain',
'command_buffer_client_nacl',
],
},
@@ -86,13 +95,16 @@
'build_glibc': 0,
'build_newlib': 0,
'build_irt': 1,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
},
'includes': [
'command_buffer_client.gypi',
],
'dependencies': [
- '../native_client/tools.gyp:prep_toolchain',
'../base/base_nacl.gyp:base_nacl',
+ '../base/base_nacl.gyp:base_nacl_nonsfi',
+ '../native_client/tools.gyp:prep_toolchain',
'command_buffer_common_nacl',
],
},
@@ -105,13 +117,16 @@
'build_glibc': 0,
'build_newlib': 0,
'build_irt': 1,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
},
'includes': [
'gpu_ipc.gypi',
],
'dependencies': [
- '../native_client/tools.gyp:prep_toolchain',
'../base/base_nacl.gyp:base_nacl',
+ '../base/base_nacl.gyp:base_nacl_nonsfi',
+ '../native_client/tools.gyp:prep_toolchain',
'command_buffer_common_nacl',
],
},
diff --git a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
index 6b91318..32b7192 100644
--- a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
+++ b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
@@ -14,9 +14,6 @@
import org.chromium.base.CalledByNative;
import org.chromium.base.CalledByNativeUnchecked;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URLConnection;
@@ -140,65 +137,6 @@
}
/**
- * @return the network interfaces list (if any) string. The items in
- * the list string are delimited by a new line, each item
- * is tab separated network interface name, address with network
- * prefix length and network interface index.
- * as "name\taddress/prefix\tindex". e.g.
- * eth0\t10.0.0.2/8\t5\neth0\tfe80::5054:ff:fe12:3456/16\t5
- * represents a network list string with two items.
- */
- @CalledByNative
- public static String getNetworkList() {
- Enumeration<NetworkInterface> list = null;
- try {
- list = NetworkInterface.getNetworkInterfaces();
- if (list == null) return "";
- } catch (SocketException e) {
- Log.w(TAG, "Unable to get network interfaces: " + e);
- return "";
- }
-
- StringBuilder result = new StringBuilder();
- while (list.hasMoreElements()) {
- NetworkInterface netIf = list.nextElement();
- try {
- // Skip loopback interfaces, and ones which are down.
- if (!netIf.isUp() || netIf.isLoopback())
- continue;
- for (InterfaceAddress interfaceAddress : netIf.getInterfaceAddresses()) {
- InetAddress address = interfaceAddress.getAddress();
- // Skip loopback addresses configured on non-loopback interfaces.
- if (address.isLoopbackAddress())
- continue;
- StringBuilder addressString = new StringBuilder();
- addressString.append(netIf.getName());
- addressString.append("\t");
-
- String ipAddress = address.getHostAddress();
- if (address instanceof Inet6Address && ipAddress.contains("%")) {
- ipAddress = ipAddress.substring(0, ipAddress.lastIndexOf("%"));
- }
- addressString.append(ipAddress);
- addressString.append("/");
- addressString.append(interfaceAddress.getNetworkPrefixLength());
- addressString.append("\t");
-
- // TODO(vitalybuka): use netIf.getIndex() when API level 19 is availible.
- addressString.append("0");
-
- if (result.length() != 0)
- result.append("\n");
- result.append(addressString.toString());
- }
- } catch (SocketException e) {
- continue;
- }
- }
- return result.toString();
- }
-
- /**
* Validate the server's certificate chain is trusted. Note that the caller
* must still verify the name matches that of the leaf certificate.
*
diff --git a/net/android/network_library.cc b/net/android/network_library.cc
index c3a42f7..0544395 100644
--- a/net/android/network_library.cc
+++ b/net/android/network_library.cc
@@ -99,13 +99,6 @@
return Java_AndroidNetworkLibrary_haveOnlyLoopbackAddresses(env);
}
-std::string GetNetworkList() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jstring> ret =
- Java_AndroidNetworkLibrary_getNetworkList(env);
- return ConvertJavaStringToUTF8(ret);
-}
-
bool GetMimeTypeFromExtension(const std::string& extension,
std::string* result) {
JNIEnv* env = AttachCurrentThread();
diff --git a/net/android/network_library.h b/net/android/network_library.h
index ba2bfb5..4a4ddca 100644
--- a/net/android/network_library.h
+++ b/net/android/network_library.h
@@ -60,12 +60,6 @@
// Also returns false if it cannot determine this.
bool HaveOnlyLoopbackAddresses();
-// Return a string containing a list of network interfaces, each item is a
-// network name and address pair.
-// e.g. "eth0,10.0.0.2;eth0,fe80::5054:ff:fe12:3456" is a result string
-// containing two items.
-std::string GetNetworkList();
-
// Get the mime type (if any) that is associated with the file extension.
// Returns true if a corresponding mime type exists.
bool GetMimeTypeFromExtension(const std::string& extension,
diff --git a/net/base/net_util_posix.cc b/net/base/net_util_posix.cc
index 73704cd..a2fa6cd 100644
--- a/net/base/net_util_posix.cc
+++ b/net/base/net_util_posix.cc
@@ -19,11 +19,16 @@
#include "net/base/net_errors.h"
#include "url/gurl.h"
-#if !defined(OS_ANDROID) && !defined(OS_NACL)
+#if !defined(OS_NACL)
+#if defined(OS_MACOSX)
#include <ifaddrs.h>
+#else
+#include "net/base/address_tracker_linux.h"
+#include "net/base/net_util_posix.h"
+#endif // OS_MACOSX
#include <net/if.h>
#include <netinet/in.h>
-#endif
+#endif // !defined(OS_NACL)
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include <net/if_media.h>
@@ -31,15 +36,48 @@
#include <sys/ioctl.h>
#endif
-#if defined(OS_ANDROID)
-#include "net/android/network_library.h"
-#endif
-
namespace net {
namespace {
-#if !defined(OS_ANDROID)
+// The application layer can pass |policy| defined in net_util.h to
+// request filtering out certain type of interfaces.
+bool ShouldIgnoreInterface(const std::string& name, int policy) {
+ // Filter out VMware interfaces, typically named vmnet1 and vmnet8,
+ // which might not be useful for use cases like WebRTC.
+ if ((policy & EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES) &&
+ ((name.find("vmnet") != std::string::npos) ||
+ (name.find("vnic") != std::string::npos))) {
+ return true;
+ }
+
+ return false;
+}
+
+// Check if the address is unspecified (i.e. made of zeroes) or loopback.
+bool IsLoopbackOrUnspecifiedAddress(const sockaddr* addr) {
+ if (addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6* addr_in6 =
+ reinterpret_cast<const struct sockaddr_in6*>(addr);
+ const struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
+ if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(sin6_addr)) {
+ return true;
+ }
+ } else if (addr->sa_family == AF_INET) {
+ const struct sockaddr_in* addr_in =
+ reinterpret_cast<const struct sockaddr_in*>(addr);
+ if (addr_in->sin_addr.s_addr == INADDR_LOOPBACK ||
+ addr_in->sin_addr.s_addr == 0) {
+ return true;
+ }
+ } else {
+ // Skip non-IP addresses.
+ return true;
+ }
+ return false;
+}
+
+#if defined(OS_MACOSX)
struct NetworkInterfaceInfo {
NetworkInterfaceInfo() : permanent(true) { }
@@ -83,10 +121,7 @@
}
}
-#endif
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-
+#if !defined(OS_IOS)
NetworkChangeNotifier::ConnectionType GetNetworkInterfaceType(
int addr_family, const std::string& interface_name) {
NetworkChangeNotifier::ConnectionType type =
@@ -111,50 +146,151 @@
return type;
}
-#endif
+#endif // !defined(OS_IOS)
+#elif !defined(OS_NACL) // OS_MACOSX
+// Convert platform native IPv6 address attributes to net IP address
+// attributes and drop ones that can't be used by the application
+// layer.
+bool TryConvertNativeToNetIPAttributes(int native_attributes,
+ int* net_attributes) {
+ // For Linux/ChromeOS/Android, we disallow addresses with attributes
+ // IFA_F_OPTIMISTIC, IFA_F_DADFAILED, and IFA_F_TENTATIVE as these
+ // are still progressing through duplicated address detection (DAD)
+ // and shouldn't be used by the application layer until DAD process
+ // is completed.
+ if (native_attributes & (
+#if !defined(OS_ANDROID)
+ IFA_F_OPTIMISTIC | IFA_F_DADFAILED |
+#endif // !OS_ANDROID
+ IFA_F_TENTATIVE)) {
+ return false;
+ }
+
+ if (native_attributes & IFA_F_TEMPORARY) {
+ *net_attributes |= IP_ADDRESS_ATTRIBUTE_TEMPORARY;
+ }
+
+ if (native_attributes & IFA_F_DEPRECATED) {
+ *net_attributes |= IP_ADDRESS_ATTRIBUTE_DEPRECATED;
+ }
+
+ return true;
+}
+#endif // OS_MACOSX
} // namespace
+namespace internal {
+
+#if !defined(OS_MACOSX) && !defined(OS_NACL)
+
+inline const unsigned char* GetIPAddressData(const IPAddressNumber& ip) {
+#if defined(OS_ANDROID)
+ return ip.begin();
+#else
+ return ip.data();
+#endif
+}
+
+bool GetNetworkListImpl(
+ NetworkInterfaceList* networks,
+ int policy,
+ const base::hash_set<int>& online_links,
+ const internal::AddressTrackerLinux::AddressMap& address_map,
+ GetInterfaceNameFunction get_interface_name) {
+ std::map<int, std::string> ifnames;
+
+ for (internal::AddressTrackerLinux::AddressMap::const_iterator it =
+ address_map.begin();
+ it != address_map.end();
+ ++it) {
+ // Ignore addresses whose links are not online.
+ if (online_links.find(it->second.ifa_index) == online_links.end())
+ continue;
+
+ sockaddr_storage sock_addr;
+ socklen_t sock_len = sizeof(sockaddr_storage);
+
+ // Convert to sockaddr for next check.
+ if (!IPEndPoint(it->first, 0)
+ .ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addr), &sock_len)) {
+ continue;
+ }
+
+ // Skip unspecified addresses (i.e. made of zeroes) and loopback addresses
+ if (IsLoopbackOrUnspecifiedAddress(reinterpret_cast<sockaddr*>(&sock_addr)))
+ continue;
+
+ int ip_attributes = IP_ADDRESS_ATTRIBUTE_NONE;
+
+ if (it->second.ifa_family == AF_INET6) {
+ // Ignore addresses whose attributes are not actionable by
+ // the application layer.
+ if (!TryConvertNativeToNetIPAttributes(it->second.ifa_flags,
+ &ip_attributes))
+ continue;
+ }
+
+ // Find the name of this link.
+ std::map<int, std::string>::const_iterator itname =
+ ifnames.find(it->second.ifa_index);
+ std::string ifname;
+ if (itname == ifnames.end()) {
+ char buffer[IF_NAMESIZE] = {0};
+ if (get_interface_name(it->second.ifa_index, buffer)) {
+ ifname = ifnames[it->second.ifa_index] = buffer;
+ } else {
+ // Ignore addresses whose interface name can't be retrieved.
+ continue;
+ }
+ } else {
+ ifname = itname->second;
+ }
+
+ // Based on the interface name and policy, determine whether we
+ // should ignore it.
+ if (ShouldIgnoreInterface(ifname, policy))
+ continue;
+
+ networks->push_back(
+ NetworkInterface(ifname,
+ ifname,
+ it->second.ifa_index,
+ NetworkChangeNotifier::CONNECTION_UNKNOWN,
+ it->first,
+ it->second.ifa_prefixlen,
+ ip_attributes));
+ }
+
+ return true;
+}
+#endif
+
+} // namespace internal
+
bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
+ if (networks == NULL)
+ return false;
#if defined(OS_NACL)
NOTIMPLEMENTED();
return false;
-#elif defined(OS_ANDROID)
- std::string network_list = android::GetNetworkList();
- base::StringTokenizer network_interfaces(network_list, "\n");
- while (network_interfaces.GetNext()) {
- std::string network_item = network_interfaces.token();
- base::StringTokenizer network_tokenizer(network_item, "\t");
- CHECK(network_tokenizer.GetNext());
- std::string name = network_tokenizer.token();
+#elif !defined(OS_MACOSX)
- CHECK(network_tokenizer.GetNext());
- std::string interface_address = network_tokenizer.token();
- IPAddressNumber address;
- size_t network_prefix = 0;
- CHECK(ParseCIDRBlock(network_tokenizer.token(),
- &address,
- &network_prefix));
+ internal::AddressTrackerLinux tracker;
+ tracker.Init();
- CHECK(network_tokenizer.GetNext());
- uint32 index = 0;
- CHECK(base::StringToUint(network_tokenizer.token(), &index));
+ return internal::GetNetworkListImpl(networks,
+ policy,
+ tracker.GetOnlineLinks(),
+ tracker.GetAddressMap(),
+ &if_indextoname);
- networks->push_back(
- NetworkInterface(name,
- name,
- index,
- NetworkChangeNotifier::CONNECTION_UNKNOWN,
- address,
- network_prefix,
- IP_ADDRESS_ATTRIBUTE_NONE));
- }
- return true;
-#else
+#else // Only OS_MACOSX and OS_IOS will run the code below
+
// getifaddrs() may require IO operations.
base::ThreadRestrictions::AssertIOAllowed();
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if !defined(OS_IOS)
int ioctl_socket = -1;
if (policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) {
// we need a socket to query information about temporary address.
@@ -163,7 +299,7 @@
}
#endif
- ifaddrs *interfaces;
+ ifaddrs* interfaces;
if (getifaddrs(&interfaces) < 0) {
PLOG(ERROR) << "getifaddrs";
return false;
@@ -187,41 +323,26 @@
// Skip unspecified addresses (i.e. made of zeroes) and loopback addresses
// configured on non-loopback interfaces.
+ if (IsLoopbackOrUnspecifiedAddress(addr))
+ continue;
+
int addr_size = 0;
if (addr->sa_family == AF_INET6) {
- struct sockaddr_in6* addr_in6 =
- reinterpret_cast<struct sockaddr_in6*>(addr);
- struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
- addr_size = sizeof(*addr_in6);
- if (IN6_IS_ADDR_LOOPBACK(sin6_addr) ||
- IN6_IS_ADDR_UNSPECIFIED(sin6_addr)) {
- continue;
- }
+ addr_size = sizeof(sockaddr_in6);
} else if (addr->sa_family == AF_INET) {
- struct sockaddr_in* addr_in =
- reinterpret_cast<struct sockaddr_in*>(addr);
- addr_size = sizeof(*addr_in);
- if (addr_in->sin_addr.s_addr == INADDR_LOOPBACK ||
- addr_in->sin_addr.s_addr == 0) {
- continue;
- }
- } else {
- // Skip non-IP addresses.
- continue;
+ addr_size = sizeof(sockaddr_in);
}
const std::string& name = interface->ifa_name;
// Filter out VMware interfaces, typically named vmnet1 and vmnet8.
- if ((policy & EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES) &&
- ((name.find("vmnet") != std::string::npos) ||
- (name.find("vnic") != std::string::npos))) {
+ if (ShouldIgnoreInterface(name, policy)) {
continue;
}
NetworkInterfaceInfo network_info;
NetworkChangeNotifier::ConnectionType connection_type =
NetworkChangeNotifier::CONNECTION_UNKNOWN;
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if !defined(OS_IOS)
// Check if this is a temporary address. Currently this is only supported
// on Mac.
if ((policy & INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE) &&
@@ -264,7 +385,7 @@
}
}
freeifaddrs(interfaces);
-#if defined(OS_MACOSX) && !defined(OS_IOS)
+#if !defined(OS_IOS)
if (ioctl_socket >= 0) {
close(ioctl_socket);
}
diff --git a/net/base/net_util_posix.h b/net/base/net_util_posix.h
new file mode 100644
index 0000000..b553d1c
--- /dev/null
+++ b/net/base/net_util_posix.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_NET_UTIL_POSIX_H_
+#define NET_BASE_NET_UTIL_POSIX_H_
+
+// This file is only used to expose some of the internals
+// of net_util_posix.cc to tests.
+
+namespace net {
+namespace internal {
+
+#if !defined(OS_MACOSX) && !defined(OS_NACL)
+typedef char* (*GetInterfaceNameFunction)(unsigned int interface_index,
+ char* ifname);
+
+NET_EXPORT bool GetNetworkListImpl(
+ NetworkInterfaceList* networks,
+ int policy,
+ const base::hash_set<int>& online_links,
+ const internal::AddressTrackerLinux::AddressMap& address_map,
+ GetInterfaceNameFunction get_interface_name);
+
+#endif // !OS_MACOSX && !OS_NACL
+
+} // namespace internal
+
+} // namespace net
+
+#endif // NET_BASE_NET_UTIL_POSIX_H_
diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc
index 4883ff2..45f0857 100644
--- a/net/base/net_util_unittest.cc
+++ b/net/base/net_util_unittest.cc
@@ -17,6 +17,11 @@
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
#include "base/time/time.h"
+
+#if !defined(OS_NACL) && !defined(OS_WIN)
+#include <net/if.h>
+#include <netinet/in.h>
+#endif // !OS_NACL && !OS_WIN
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -25,10 +30,16 @@
#include <objbase.h>
#include "base/win/windows_version.h"
#include "net/base/net_util_win.h"
-#elif !defined(OS_ANDROID)
-#include <net/if.h>
#endif // OS_WIN
+#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_WIN)
+#include "net/base/address_tracker_linux.h"
+#endif // !OS_MACOSX && !OS_NACL && !OS_WIN
+
+#if !defined(OS_WIN)
+#include "net/base/net_util_posix.h"
+#endif // !OS_WIN
+
using base::ASCIIToUTF16;
using base::WideToUTF16;
@@ -801,6 +812,152 @@
}
}
+#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_NACL)
+
+char* CopyInterfaceName(const char* ifname, int ifname_size, char* output) {
+ EXPECT_LT(ifname_size, IF_NAMESIZE);
+ memcpy(output, ifname, ifname_size);
+ return output;
+}
+
+static const char ifname_em1[] = "em1";
+char* GetInterfaceName(unsigned int interface_index, char* ifname) {
+ return CopyInterfaceName(ifname_em1, arraysize(ifname_em1), ifname);
+}
+
+static const char ifname_vm[] = "vmnet";
+char* GetInterfaceNameVM(unsigned int interface_index, char* ifname) {
+ return CopyInterfaceName(ifname_vm, arraysize(ifname_vm), ifname);
+}
+
+TEST(NetUtilTest, GetNetworkListTrimming) {
+ NetworkInterfaceList results;
+ ::base::hash_set<int> online_links;
+ net::internal::AddressTrackerLinux::AddressMap address_map;
+
+ const unsigned char kIPv6LocalAddr[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ const unsigned char kIPv6Addr[] =
+ {0x24, 0x01, 0xfa, 0x00, 0x00, 0x04, 0x10, 0x00, 0xbe, 0x30, 0x5b, 0xff,
+ 0xfe, 0xe5, 0x00, 0xc3};
+
+ IPAddressNumber ipv6_local_address(
+ kIPv6LocalAddr, kIPv6LocalAddr + arraysize(kIPv6LocalAddr));
+ IPAddressNumber ipv6_address(kIPv6Addr, kIPv6Addr + arraysize(kIPv6Addr));
+
+ // Interface 1 is offline.
+ struct ifaddrmsg msg = {
+ AF_INET6,
+ 1, /* prefix length */
+ IFA_F_TEMPORARY, /* address flags */
+ 0, /* link scope */
+ 1 /* link index */
+ };
+
+ // Address of offline links should be ignored.
+ ASSERT_TRUE(address_map.insert(std::make_pair(ipv6_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceName));
+ EXPECT_EQ(results.size(), 0ul);
+
+ // Mark interface 1 online.
+ online_links.insert(1);
+
+ // Local address should be trimmed out.
+ address_map.clear();
+ ASSERT_TRUE(
+ address_map.insert(std::make_pair(ipv6_local_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceName));
+ EXPECT_EQ(results.size(), 0ul);
+
+ // vmware address should return by default.
+ address_map.clear();
+ ASSERT_TRUE(address_map.insert(std::make_pair(ipv6_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceNameVM));
+ EXPECT_EQ(results.size(), 1ul);
+ EXPECT_EQ(results[0].name, ifname_vm);
+ EXPECT_EQ(results[0].network_prefix, 1ul);
+ EXPECT_EQ(results[0].address, ipv6_address);
+ results.clear();
+
+ // vmware address should be trimmed out if policy specified so.
+ address_map.clear();
+ ASSERT_TRUE(address_map.insert(std::make_pair(ipv6_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceNameVM));
+ EXPECT_EQ(results.size(), 0ul);
+ results.clear();
+
+ // Addresses with banned attributes should be ignored.
+ address_map.clear();
+ msg.ifa_flags = IFA_F_TENTATIVE;
+ ASSERT_TRUE(address_map.insert(std::make_pair(ipv6_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceName));
+ EXPECT_EQ(results.size(), 0ul);
+ results.clear();
+
+ // Addresses with allowed attribute IFA_F_TEMPORARY should be returned and
+ // attributes should be translated correctly.
+ address_map.clear();
+ msg.ifa_flags = IFA_F_TEMPORARY;
+ ASSERT_TRUE(address_map.insert(std::make_pair(ipv6_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceName));
+ EXPECT_EQ(results.size(), 1ul);
+ EXPECT_EQ(results[0].name, ifname_em1);
+ EXPECT_EQ(results[0].network_prefix, 1ul);
+ EXPECT_EQ(results[0].address, ipv6_address);
+ EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_TEMPORARY);
+ results.clear();
+
+ // Addresses with allowed attribute IFA_F_DEPRECATED should be returned and
+ // attributes should be translated correctly.
+ address_map.clear();
+ msg.ifa_flags = IFA_F_DEPRECATED;
+ ASSERT_TRUE(address_map.insert(std::make_pair(ipv6_address, msg)).second);
+ EXPECT_TRUE(
+ net::internal::GetNetworkListImpl(&results,
+ INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+ online_links,
+ address_map,
+ GetInterfaceName));
+ EXPECT_EQ(results.size(), 1ul);
+ EXPECT_EQ(results[0].name, ifname_em1);
+ EXPECT_EQ(results[0].network_prefix, 1ul);
+ EXPECT_EQ(results[0].address, ipv6_address);
+ EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_DEPRECATED);
+ results.clear();
+}
+
+#endif
+
namespace {
#if defined(OS_WIN)
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
index 0f31d5c..2a6ef4d 100644
--- a/net/base/sdch_manager.cc
+++ b/net/base/sdch_manager.cc
@@ -54,7 +54,12 @@
bool SdchManager::g_sdch_enabled_ = true;
// static
+#if defined(OS_IOS)
+// Workaround for http://crbug.com/418975; remove when fixed.
+bool SdchManager::g_secure_scheme_supported_ = false;
+#else
bool SdchManager::g_secure_scheme_supported_ = true;
+#endif
//------------------------------------------------------------------------------
SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
diff --git a/net/base/sdch_manager_unittest.cc b/net/base/sdch_manager_unittest.cc
index f6d44be..f1b39e7 100644
--- a/net/base/sdch_manager_unittest.cc
+++ b/net/base/sdch_manager_unittest.cc
@@ -518,12 +518,21 @@
GURL url("http://www.google.com");
GURL secure_url("https://www.google.com");
- EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(url));
- EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(secure_url));
+#if !defined(OS_IOS)
+ // Workaround for http://crbug.com/418975; remove when fixed.
+ bool expect_https_support = true;
+#else
+ bool expect_https_support = false;
+#endif
- SdchManager::EnableSecureSchemeSupport(false);
EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(url));
- EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(secure_url));
+ EXPECT_EQ(expect_https_support,
+ sdch_manager()->IsInSupportedDomain(secure_url));
+
+ SdchManager::EnableSecureSchemeSupport(!expect_https_support);
+ EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(url));
+ EXPECT_NE(expect_https_support,
+ sdch_manager()->IsInSupportedDomain(secure_url));
}
TEST_F(SdchManagerTest, ClearDictionaryData) {
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 5441e90..c39a38a 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -1208,6 +1208,7 @@
UpdateTransactionPattern(PATTERN_ENTRY_NOT_CACHED);
}
+ // Invalidate any cached GET with a successful PUT or DELETE.
if (mode_ == WRITE &&
(request_->method == "PUT" || request_->method == "DELETE")) {
if (NonErrorResponse(new_response->headers->response_code())) {
@@ -1219,7 +1220,9 @@
mode_ = NONE;
}
- if (request_->method == "POST" &&
+ // Invalidate any cached GET with a successful POST.
+ if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) &&
+ request_->method == "POST" &&
NonErrorResponse(new_response->headers->response_code())) {
cache_->DoomMainEntryForUrl(request_->url);
}
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index a0225e8..5199368 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -2741,6 +2741,18 @@
EXPECT_EQ(0, cache.disk_cache()->create_count());
}
+// Tests POST handling with a disabled cache (no DCHECK).
+TEST(HttpCache, SimplePOST_DisabledCache) {
+ MockHttpCache cache;
+ cache.http_cache()->set_mode(net::HttpCache::Mode::DISABLE);
+
+ RunTransactionTest(cache.http_cache(), kSimplePOST_Transaction);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(0, cache.disk_cache()->create_count());
+}
+
TEST(HttpCache, SimplePOST_LoadOnlyFromCache_Miss) {
MockHttpCache cache;
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 27afbea..205a571 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -47,7 +47,6 @@
base::Value* NetLogQuicPacketSentCallback(
const SerializedPacket& serialized_packet,
- QuicPacketSequenceNumber original_sequence_number,
EncryptionLevel level,
TransmissionType transmission_type,
size_t packet_size,
@@ -58,13 +57,23 @@
dict->SetInteger("transmission_type", transmission_type);
dict->SetString("packet_sequence_number",
base::Uint64ToString(serialized_packet.sequence_number));
- dict->SetString("original_sequence_number",
- base::Uint64ToString(original_sequence_number));
dict->SetInteger("size", packet_size);
dict->SetInteger("sent_time_us", sent_time.ToDebuggingValue());
return dict;
}
+base::Value* NetLogQuicPacketRetransmittedCallback(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("old_packet_sequence_number",
+ base::Uint64ToString(old_sequence_number));
+ dict->SetString("new_packet_sequence_number",
+ base::Uint64ToString(new_sequence_number));
+ return dict;
+}
+
base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header,
NetLog::LogLevel /* log_level */) {
base::DictionaryValue* dict = new base::DictionaryValue();
@@ -480,11 +489,18 @@
TransmissionType transmission_type,
const QuicEncryptedPacket& packet,
QuicTime sent_time) {
- net_log_.AddEvent(
- NetLog::TYPE_QUIC_SESSION_PACKET_SENT,
- base::Bind(&NetLogQuicPacketSentCallback, serialized_packet,
- original_sequence_number, level, transmission_type,
- packet.length(), sent_time));
+ if (original_sequence_number == 0) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_PACKET_SENT,
+ base::Bind(&NetLogQuicPacketSentCallback, serialized_packet,
+ level, transmission_type, packet.length(), sent_time));
+ } else {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_PACKET_RETRANSMITTED,
+ base::Bind(&NetLogQuicPacketRetransmittedCallback,
+ original_sequence_number,
+ serialized_packet.sequence_number));
+ }
}
void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address,
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index 4712620..77a9e89 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -730,7 +730,7 @@
QuicStreamId stream_id = 5;
// Write until the header stream is flow control blocked.
SpdyHeaderBlock headers;
- while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2010) {
+ while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2000) {
EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
headers["header"] = base::Uint64ToString(base::RandUint64()) +
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index 953f336..fc74d8c 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -632,7 +632,7 @@
user_read_buf_ = buf;
user_read_buf_len_ = buf_len;
- int rv = DoReadLoop(OK);
+ int rv = DoReadLoop();
if (rv == ERR_IO_PENDING) {
user_read_callback_ = callback;
@@ -657,7 +657,7 @@
user_write_buf_ = buf;
user_write_buf_len_ = buf_len;
- int rv = DoWriteLoop(OK);
+ int rv = DoWriteLoop();
if (rv == ERR_IO_PENDING) {
user_write_callback_ = callback;
@@ -1236,7 +1236,7 @@
if (!user_read_buf_.get())
return;
- int rv = DoReadLoop(result);
+ int rv = DoReadLoop();
if (rv != ERR_IO_PENDING)
DoReadCallback(rv);
}
@@ -1288,10 +1288,7 @@
return rv;
}
-int SSLClientSocketOpenSSL::DoReadLoop(int result) {
- if (result < 0)
- return result;
-
+int SSLClientSocketOpenSSL::DoReadLoop() {
bool network_moved;
int rv;
do {
@@ -1302,10 +1299,7 @@
return rv;
}
-int SSLClientSocketOpenSSL::DoWriteLoop(int result) {
- if (result < 0)
- return result;
-
+int SSLClientSocketOpenSSL::DoWriteLoop() {
bool network_moved;
int rv;
do {
diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h
index 49a2b45..8ed45c3 100644
--- a/net/socket/ssl_client_socket_openssl.h
+++ b/net/socket/ssl_client_socket_openssl.h
@@ -130,8 +130,8 @@
void OnRecvComplete(int result);
int DoHandshakeLoop(int last_io_result);
- int DoReadLoop(int result);
- int DoWriteLoop(int result);
+ int DoReadLoop();
+ int DoWriteLoop();
int DoPayloadRead();
int DoPayloadWrite();
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 1dd9fb4..16e03f7 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -1873,6 +1873,54 @@
EXPECT_EQ(0, rv);
}
+// Tests that SSLClientSocket cleanly returns a Read of size 0 if the
+// underlying socket is cleanly closed asynchronously.
+// This is a regression test for https://crbug.com/422246
+TEST_F(SSLClientSocketTest, Read_WithAsyncZeroReturn) {
+ SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
+ SpawnedTestServer::kLocalhost,
+ base::FilePath());
+ ASSERT_TRUE(test_server.Start());
+
+ AddressList addr;
+ ASSERT_TRUE(test_server.GetAddressList(&addr));
+
+ TestCompletionCallback callback;
+ scoped_ptr<StreamSocket> real_transport(
+ new TCPClientSocket(addr, NULL, NetLog::Source()));
+ scoped_ptr<SynchronousErrorStreamSocket> error_socket(
+ new SynchronousErrorStreamSocket(real_transport.Pass()));
+ SynchronousErrorStreamSocket* raw_error_socket = error_socket.get();
+ scoped_ptr<FakeBlockingStreamSocket> transport(
+ new FakeBlockingStreamSocket(error_socket.Pass()));
+ FakeBlockingStreamSocket* raw_transport = transport.get();
+ int rv = callback.GetResult(transport->Connect(callback.callback()));
+ EXPECT_EQ(OK, rv);
+
+ // Disable TLS False Start to ensure the handshake has completed.
+ SSLConfig ssl_config;
+ ssl_config.false_start_enabled = false;
+
+ scoped_ptr<SSLClientSocket> sock(
+ CreateSSLClientSocket(transport.Pass(),
+ test_server.host_port_pair(),
+ ssl_config));
+
+ rv = callback.GetResult(sock->Connect(callback.callback()));
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(sock->IsConnected());
+
+ raw_error_socket->SetNextReadError(0);
+ raw_transport->BlockReadResult();
+ scoped_refptr<IOBuffer> buf(new IOBuffer(4096));
+ rv = sock->Read(buf.get(), 4096, callback.callback());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ raw_transport->UnblockReadResult();
+ rv = callback.GetResult(rv);
+ EXPECT_EQ(0, rv);
+}
+
TEST_F(SSLClientSocketTest, Read_SmallChunks) {
SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS,
SpawnedTestServer::kLocalhost,
diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc
index 61f8c01..5b5c415 100644
--- a/net/tools/quic/quic_client_session_test.cc
+++ b/net/tools/quic/quic_client_session_test.cc
@@ -33,7 +33,7 @@
namespace test {
namespace {
-const char kServerHostname[] = "www.example.com";
+const char kServerHostname[] = "www.example.org";
const uint16 kPort = 80;
class ToolsQuicClientSessionTest
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index 4294414..2fc58cf 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -358,6 +358,7 @@
'gif': 'image/gif',
'jpeg' : 'image/jpeg',
'jpg' : 'image/jpeg',
+ 'js' : 'application/javascript',
'json': 'application/json',
'pdf' : 'application/pdf',
'txt' : 'text/plain',
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
new file mode 100644
index 0000000..646630c
--- /dev/null
+++ b/sandbox/linux/BUILD.gn
@@ -0,0 +1,322 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+
+declare_args() {
+ compile_suid_client = is_linux
+
+ compile_credentials = is_linux
+
+ compile_seccomp_bpf_demo =
+ (is_linux && (cpu_arch == "x86" || cpu_arch == "x64"))
+}
+
+# We have two principal targets: sandbox and sandbox_linux_unittests
+# All other targets are listed as dependencies.
+# There is one notable exception: for historical reasons, chrome_sandbox is
+# the setuid sandbox and is its own target.
+
+group("sandbox") {
+ deps = [
+ ":sandbox_services",
+ ]
+
+ if (compile_suid_client) {
+ deps += [ ":suid_sandbox_client" ]
+ }
+ if (use_seccomp_bpf) {
+ deps += [
+ ":seccomp_bpf",
+ ":seccomp_bpf_helpers",
+ ]
+ }
+}
+
+source_set("sandbox_linux_test_utils") {
+ testonly = true
+ sources = [
+ "tests/sandbox_test_runner.cc",
+ "tests/sandbox_test_runner.h",
+ "tests/sandbox_test_runner_function_pointer.cc",
+ "tests/sandbox_test_runner_function_pointer.h",
+ "tests/test_utils.cc",
+ "tests/test_utils.h",
+ "tests/unit_tests.cc",
+ "tests/unit_tests.h",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ if (use_seccomp_bpf) {
+ sources += [
+ "seccomp-bpf/bpf_tester_compatibility_delegate.h",
+ "seccomp-bpf/bpf_tests.h",
+ "seccomp-bpf/sandbox_bpf_test_runner.cc",
+ "seccomp-bpf/sandbox_bpf_test_runner.h",
+ ]
+ deps += [
+ ":seccomp_bpf",
+ ]
+ }
+}
+
+# The main sandboxing test target.
+test("sandbox_linux_unittests") {
+ sources = [
+ "services/broker_process_unittest.cc",
+ "services/scoped_process_unittest.cc",
+ "services/thread_helpers_unittests.cc",
+ "services/yama_unittests.cc",
+ "tests/main.cc",
+ "tests/scoped_temporary_file.cc",
+ "tests/scoped_temporary_file.h",
+ "tests/scoped_temporary_file_unittest.cc",
+ "tests/unit_tests_unittest.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ ":sandbox_linux_test_utils",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+
+ if (is_linux) {
+ # Don't use this on Android.
+ libs = [ "rt" ]
+ }
+
+ if (compile_suid_client) {
+ sources += [
+ "suid/client/setuid_sandbox_client_unittest.cc",
+ ]
+ }
+ if (use_seccomp_bpf) {
+ sources += [
+ "bpf_dsl/bpf_dsl_more_unittest.cc",
+ "bpf_dsl/bpf_dsl_unittest.cc",
+ "bpf_dsl/cons_unittest.cc",
+ "seccomp-bpf-helpers/baseline_policy_unittest.cc",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc",
+ "seccomp-bpf/bpf_tests_unittest.cc",
+ "seccomp-bpf/codegen_unittest.cc",
+ "seccomp-bpf/errorcode_unittest.cc",
+ "seccomp-bpf/syscall_iterator_unittest.cc",
+ "seccomp-bpf/syscall_unittest.cc",
+ ]
+ }
+ if (compile_credentials) {
+ sources += [
+ "services/credentials_unittest.cc",
+ "services/unix_domain_socket_unittest.cc",
+ ]
+ }
+}
+
+# TODO(GYP) Android version of this test.
+# {
+# # This target is the shared library used by Android APK (i.e.
+# # JNI-friendly) tests.
+# "target_name": "sandbox_linux_jni_unittests",
+# "includes": [
+# "sandbox_linux_test_sources.gypi",
+# ],
+# "type": "shared_library",
+# "conditions": [
+# [ "OS == "android"", {
+# "dependencies": [
+# "../testing/android/native_test.gyp:native_test_native_code",
+# ],
+# }],
+# ],
+# },
+
+component("seccomp_bpf") {
+ sources = [
+ "bpf_dsl/bpf_dsl.cc",
+ "bpf_dsl/bpf_dsl.h",
+ "bpf_dsl/bpf_dsl_impl.h",
+ "bpf_dsl/cons.h",
+ "bpf_dsl/policy_compiler.cc",
+ "bpf_dsl/policy_compiler.h",
+ "bpf_dsl/trap_registry.h",
+ "seccomp-bpf/basicblock.cc",
+ "seccomp-bpf/basicblock.h",
+ "seccomp-bpf/codegen.cc",
+ "seccomp-bpf/codegen.h",
+ "seccomp-bpf/die.cc",
+ "seccomp-bpf/die.h",
+ "seccomp-bpf/errorcode.cc",
+ "seccomp-bpf/errorcode.h",
+ "seccomp-bpf/instruction.h",
+ "seccomp-bpf/linux_seccomp.h",
+ "seccomp-bpf/sandbox_bpf.cc",
+ "seccomp-bpf/sandbox_bpf.h",
+ "seccomp-bpf/syscall.cc",
+ "seccomp-bpf/syscall.h",
+ "seccomp-bpf/syscall_iterator.cc",
+ "seccomp-bpf/syscall_iterator.h",
+ "seccomp-bpf/trap.cc",
+ "seccomp-bpf/trap.h",
+ "seccomp-bpf/verifier.cc",
+ "seccomp-bpf/verifier.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services_headers",
+ "//base",
+ ]
+}
+
+component("seccomp_bpf_helpers") {
+ sources = [
+ "seccomp-bpf-helpers/baseline_policy.cc",
+ "seccomp-bpf-helpers/baseline_policy.h",
+ "seccomp-bpf-helpers/sigsys_handlers.cc",
+ "seccomp-bpf-helpers/sigsys_handlers.h",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions.cc",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions.h",
+ "seccomp-bpf-helpers/syscall_sets.cc",
+ "seccomp-bpf-helpers/syscall_sets.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ ":seccomp_bpf",
+ ]
+}
+
+if (is_linux) {
+# The setuid sandbox for Linux.
+ executable("chrome_sandbox") {
+ sources = [
+ "suid/common/sandbox.h",
+ "suid/common/suid_unsafe_environment_variables.h",
+ "suid/process_util.h",
+ "suid/process_util_linux.c",
+ "suid/sandbox.c",
+ ]
+
+ cflags = [
+ # For ULLONG_MAX
+ "-std=gnu99",
+ # These files have a suspicious comparison.
+ # TODO fix this and re-enable this warning.
+ "-Wno-sign-compare",
+ ]
+ }
+}
+
+component("sandbox_services") {
+ sources = [
+ "services/broker_process.cc",
+ "services/broker_process.h",
+ "services/init_process_reaper.cc",
+ "services/init_process_reaper.h",
+ "services/scoped_process.cc",
+ "services/scoped_process.h",
+ "services/thread_helpers.cc",
+ "services/thread_helpers.h",
+ "services/yama.h",
+ "services/yama.cc",
+ ]
+
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ if (compile_credentials) {
+ sources += [
+ "services/credentials.cc",
+ "services/credentials.h",
+ ]
+ # For capabilities.cc.
+ configs += [ "//build/config/linux:libcap" ]
+ }
+
+ deps = [
+ "//base",
+ ]
+}
+
+source_set("sandbox_services_headers") {
+ sources = [
+ "services/android_arm_ucontext.h",
+ "services/android_arm64_ucontext.h",
+ "services/android_futex.h",
+ "services/android_ucontext.h",
+ "services/android_i386_ucontext.h",
+ "services/arm_linux_syscalls.h",
+ "services/arm64_linux_syscalls.h",
+ "services/linux_syscalls.h",
+ "services/x86_32_linux_syscalls.h",
+ "services/x86_64_linux_syscalls.h",
+ ]
+}
+
+# We make this its own target so that it does not interfere with our tests.
+source_set("libc_urandom_override") {
+ sources = [
+ "services/libc_urandom_override.cc",
+ "services/libc_urandom_override.h",
+ ]
+ deps = [
+ "//base",
+ ]
+}
+
+if (compile_suid_client) {
+ component("suid_sandbox_client") {
+ sources = [
+ "suid/common/sandbox.h",
+ "suid/common/suid_unsafe_environment_variables.h",
+ "suid/client/setuid_sandbox_client.cc",
+ "suid/client/setuid_sandbox_client.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services",
+ "//base",
+ ]
+ }
+}
+
+if (is_android) {
+ # TODO(GYP) enable this. Needs an android_strip wrapper python script.
+ #action("sandbox_linux_unittests_stripped") {
+ # script = "android_stip.py"
+ #
+ # in_file = "$root_out_dir/sandbox_linux_unittests"
+ #
+ # out_file = "$root_out_dir/sandbox_linux_unittests_stripped"
+ # outputs = [ out_file ]
+ #
+ # args = [
+ # rebase_path(in_file, root_build_dir),
+ # "-o", rebase_path(out_file, root_build_dir),
+ # ]
+ #
+ # deps = [
+ # ":sandbox_linux_unittests",
+ # ]
+ #}
+
+ # TODO(GYP) convert this.
+ # {
+ # 'target_name': 'sandbox_linux_jni_unittests_apk',
+ # 'type': 'none',
+ # 'variables': {
+ # 'test_suite_name': 'sandbox_linux_jni_unittests',
+ # },
+ # 'dependencies': [
+ # 'sandbox_linux_jni_unittests',
+ # ],
+ # 'includes': [ '../../build/apk_test.gypi' ],
+ # }
+}
diff --git a/sandbox/linux/DEPS b/sandbox/linux/DEPS
new file mode 100644
index 0000000..3912859
--- /dev/null
+++ b/sandbox/linux/DEPS
@@ -0,0 +1,25 @@
+include_rules = [
+ # First, exclude everything.
+ # Exclude a few dependencies that are included in the root DEPS and that we
+ # don't need.
+ # Sadly, there is no way to exclude all root DEPS since the root has no name.
+ "-ipc",
+ "-library_loaders",
+ "-third_party",
+ "-url",
+ # Make sure that each subdirectory has to declare its dependencies in
+ # sandbox/ explicitly.
+ "-sandbox/linux",
+
+ # Second, add what we want to allow.
+ # Anything included from sandbox/linux must be declared after this line or in
+ # a more specific DEPS file.
+ # base/, build/ and testing/ are already included in the global DEPS file,
+ # but be explicit.
+ "+base",
+ "+build",
+ "+testing",
+ "+sandbox/sandbox_export.h",
+ # Everyone can use tests/
+ "+sandbox/linux/tests",
+]
diff --git a/sandbox/linux/OWNERS b/sandbox/linux/OWNERS
new file mode 100644
index 0000000..f39e967
--- /dev/null
+++ b/sandbox/linux/OWNERS
@@ -0,0 +1,3 @@
+jln@chromium.org
+jorgelo@chromium.org
+mdempsky@chromium.org
diff --git a/sandbox/linux/bpf_dsl/DEPS b/sandbox/linux/bpf_dsl/DEPS
new file mode 100644
index 0000000..f8b9aba
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ # TODO(mdempsky): Eliminate cyclic dependency on seccomp-bpf.
+ "+sandbox/linux/seccomp-bpf",
+ "+sandbox/linux/services", # for bpf_dsl_more_unittest.cc
+]
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.cc b/sandbox/linux/bpf_dsl/bpf_dsl.cc
new file mode 100644
index 0000000..ffabeac
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -0,0 +1,388 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+class AllowResultExprImpl : public internal::ResultExprImpl {
+ public:
+ AllowResultExprImpl() {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+
+ private:
+ virtual ~AllowResultExprImpl() {}
+
+ DISALLOW_COPY_AND_ASSIGN(AllowResultExprImpl);
+};
+
+class ErrorResultExprImpl : public internal::ResultExprImpl {
+ public:
+ explicit ErrorResultExprImpl(int err) : err_(err) {
+ CHECK(err_ >= ErrorCode::ERR_MIN_ERRNO && err_ <= ErrorCode::ERR_MAX_ERRNO);
+ }
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Error(err_);
+ }
+
+ private:
+ virtual ~ErrorResultExprImpl() {}
+
+ int err_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrorResultExprImpl);
+};
+
+class KillResultExprImpl : public internal::ResultExprImpl {
+ public:
+ explicit KillResultExprImpl(const char* msg) : msg_(msg) { DCHECK(msg_); }
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Kill(msg_);
+ }
+
+ private:
+ virtual ~KillResultExprImpl() {}
+
+ const char* msg_;
+
+ DISALLOW_COPY_AND_ASSIGN(KillResultExprImpl);
+};
+
+class TraceResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TraceResultExprImpl(uint16_t aux) : aux_(aux) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return ErrorCode(ErrorCode::ERR_TRACE + aux_);
+ }
+
+ private:
+ virtual ~TraceResultExprImpl() {}
+
+ uint16_t aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceResultExprImpl);
+};
+
+class TrapResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg)
+ : func_(func), arg_(arg) {
+ DCHECK(func_);
+ }
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Trap(func_, arg_);
+ }
+
+ private:
+ virtual ~TrapResultExprImpl() {}
+
+ TrapRegistry::TrapFnc func_;
+ const void* arg_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
+};
+
+class UnsafeTrapResultExprImpl : public internal::ResultExprImpl {
+ public:
+ UnsafeTrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg)
+ : func_(func), arg_(arg) {
+ DCHECK(func_);
+ }
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->UnsafeTrap(func_, arg_);
+ }
+
+ virtual bool HasUnsafeTraps() const override { return true; }
+
+ private:
+ virtual ~UnsafeTrapResultExprImpl() {}
+
+ TrapRegistry::TrapFnc func_;
+ const void* arg_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnsafeTrapResultExprImpl);
+};
+
+class IfThenResultExprImpl : public internal::ResultExprImpl {
+ public:
+ IfThenResultExprImpl(const BoolExpr& cond,
+ const ResultExpr& then_result,
+ const ResultExpr& else_result)
+ : cond_(cond), then_result_(then_result), else_result_(else_result) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc) const override {
+ return cond_->Compile(
+ pc, then_result_->Compile(pc), else_result_->Compile(pc));
+ }
+
+ virtual bool HasUnsafeTraps() const override {
+ return then_result_->HasUnsafeTraps() || else_result_->HasUnsafeTraps();
+ }
+
+ private:
+ virtual ~IfThenResultExprImpl() {}
+
+ BoolExpr cond_;
+ ResultExpr then_result_;
+ ResultExpr else_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(IfThenResultExprImpl);
+};
+
+class ConstBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ ConstBoolExprImpl(bool value) : value_(value) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return value_ ? true_ec : false_ec;
+ }
+
+ private:
+ virtual ~ConstBoolExprImpl() {}
+
+ bool value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstBoolExprImpl);
+};
+
+class PrimitiveBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ PrimitiveBoolExprImpl(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value)
+ : argno_(argno), is_32bit_(is_32bit), mask_(mask), value_(value) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return pc->CondMaskedEqual(
+ argno_, is_32bit_, mask_, value_, true_ec, false_ec);
+ }
+
+ private:
+ virtual ~PrimitiveBoolExprImpl() {}
+
+ int argno_;
+ ErrorCode::ArgType is_32bit_;
+ uint64_t mask_;
+ uint64_t value_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveBoolExprImpl);
+};
+
+class NegateBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ explicit NegateBoolExprImpl(const BoolExpr& cond) : cond_(cond) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return cond_->Compile(pc, false_ec, true_ec);
+ }
+
+ private:
+ virtual ~NegateBoolExprImpl() {}
+
+ BoolExpr cond_;
+
+ DISALLOW_COPY_AND_ASSIGN(NegateBoolExprImpl);
+};
+
+class AndBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ AndBoolExprImpl(const BoolExpr& lhs, const BoolExpr& rhs)
+ : lhs_(lhs), rhs_(rhs) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return lhs_->Compile(pc, rhs_->Compile(pc, true_ec, false_ec), false_ec);
+ }
+
+ private:
+ virtual ~AndBoolExprImpl() {}
+
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndBoolExprImpl);
+};
+
+class OrBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ OrBoolExprImpl(const BoolExpr& lhs, const BoolExpr& rhs)
+ : lhs_(lhs), rhs_(rhs) {}
+
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return lhs_->Compile(pc, true_ec, rhs_->Compile(pc, true_ec, false_ec));
+ }
+
+ private:
+ virtual ~OrBoolExprImpl() {}
+
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrBoolExprImpl);
+};
+
+} // namespace
+
+namespace internal {
+
+bool ResultExprImpl::HasUnsafeTraps() const {
+ return false;
+}
+
+uint64_t DefaultMask(size_t size) {
+ switch (size) {
+ case 4:
+ return std::numeric_limits<uint32_t>::max();
+ case 8:
+ return std::numeric_limits<uint64_t>::max();
+ default:
+ CHECK(false) << "Unimplemented DefaultMask case";
+ return 0;
+ }
+}
+
+BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) {
+ CHECK(size == 4 || size == 8);
+
+ // TODO(mdempsky): Should we just always use TP_64BIT?
+ const ErrorCode::ArgType arg_type =
+ (size == 4) ? ErrorCode::TP_32BIT : ErrorCode::TP_64BIT;
+
+ return BoolExpr(new const PrimitiveBoolExprImpl(num, arg_type, mask, val));
+}
+
+} // namespace internal
+
+ResultExpr Allow() {
+ return ResultExpr(new const AllowResultExprImpl());
+}
+
+ResultExpr Error(int err) {
+ return ResultExpr(new const ErrorResultExprImpl(err));
+}
+
+ResultExpr Kill(const char* msg) {
+ return ResultExpr(new const KillResultExprImpl(msg));
+}
+
+ResultExpr Trace(uint16_t aux) {
+ return ResultExpr(new const TraceResultExprImpl(aux));
+}
+
+ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return ResultExpr(new const TrapResultExprImpl(trap_func, aux));
+}
+
+ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return ResultExpr(new const UnsafeTrapResultExprImpl(trap_func, aux));
+}
+
+BoolExpr BoolConst(bool value) {
+ return BoolExpr(new const ConstBoolExprImpl(value));
+}
+
+BoolExpr operator!(const BoolExpr& cond) {
+ return BoolExpr(new const NegateBoolExprImpl(cond));
+}
+
+BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs) {
+ return BoolExpr(new const AndBoolExprImpl(lhs, rhs));
+}
+
+BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs) {
+ return BoolExpr(new const OrBoolExprImpl(lhs, rhs));
+}
+
+Elser If(const BoolExpr& cond, const ResultExpr& then_result) {
+ return Elser(nullptr).ElseIf(cond, then_result);
+}
+
+Elser::Elser(cons::List<Clause> clause_list) : clause_list_(clause_list) {
+}
+
+Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) {
+}
+
+Elser::~Elser() {
+}
+
+Elser Elser::ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const {
+ return Elser(Cons(std::make_pair(cond, then_result), clause_list_));
+}
+
+ResultExpr Elser::Else(const ResultExpr& else_result) const {
+ // We finally have the default result expression for this
+ // if/then/else sequence. Also, we've already accumulated all
+ // if/then pairs into a list of reverse order (i.e., lower priority
+ // conditions are listed before higher priority ones). E.g., an
+ // expression like
+ //
+ // If(b1, e1).ElseIf(b2, e2).ElseIf(b3, e3).Else(e4)
+ //
+ // will have built up a list like
+ //
+ // [(b3, e3), (b2, e2), (b1, e1)].
+ //
+ // Now that we have e4, we can walk the list and create a ResultExpr
+ // tree like:
+ //
+ // expr = e4
+ // expr = (b3 ? e3 : expr) = (b3 ? e3 : e4)
+ // expr = (b2 ? e2 : expr) = (b2 ? e2 : (b3 ? e3 : e4))
+ // expr = (b1 ? e1 : expr) = (b1 ? e1 : (b2 ? e2 : (b3 ? e3 : e4)))
+ //
+ // and end up with an appropriately chained tree.
+
+ ResultExpr expr = else_result;
+ for (const Clause& clause : clause_list_) {
+ expr = ResultExpr(
+ new const IfThenResultExprImpl(clause.first, clause.second, expr));
+ }
+ return expr;
+}
+
+ResultExpr SandboxBPFDSLPolicy::InvalidSyscall() const {
+ return Error(ENOSYS);
+}
+
+ResultExpr SandboxBPFDSLPolicy::Trap(TrapRegistry::TrapFnc trap_func,
+ const void* aux) {
+ return bpf_dsl::Trap(trap_func, aux);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+template class scoped_refptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+template class scoped_refptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
new file mode 100644
index 0000000..25892b7
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -0,0 +1,360 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/cons.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/sandbox_export.h"
+
+// The sandbox::bpf_dsl namespace provides a domain-specific language
+// to make writing BPF policies more expressive. In general, the
+// object types all have value semantics (i.e., they can be copied
+// around, returned from or passed to function calls, etc. without any
+// surprising side effects), though not all support assignment.
+//
+// An idiomatic and demonstrative (albeit silly) example of this API
+// would be:
+//
+// #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+//
+// using namespace sandbox::bpf_dsl;
+//
+// class SillyPolicy : public SandboxBPFDSLPolicy {
+// public:
+// SillyPolicy() {}
+// virtual ~SillyPolicy() {}
+// virtual ResultExpr EvaluateSyscall(int sysno) const override {
+// if (sysno == __NR_fcntl) {
+// Arg<int> fd(0), cmd(1);
+// Arg<unsigned long> flags(2);
+// const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
+// return If(fd == 0 && cmd == F_SETFL && (flags & ~kGoodFlags) == 0,
+// Allow())
+// .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC,
+// Error(EMFILE))
+// .Else(Trap(SetFlagHandler, NULL));
+// } else {
+// return Allow();
+// }
+// }
+//
+// private:
+// DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
+// };
+//
+// More generally, the DSL currently supports the following grammar:
+//
+// result = Allow() | Error(errno) | Kill(msg) | Trace(aux)
+// | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux)
+// | If(bool, result)[.ElseIf(bool, result)].Else(result)
+// | Switch(arg)[.Case(val, result)].Default(result)
+// bool = BoolConst(boolean) | !bool | bool && bool | bool || bool
+// | arg == val | arg != val
+// arg = Arg<T>(num) | arg & mask
+//
+// The semantics of each function and operator are intended to be
+// intuitive, but are described in more detail below.
+//
+// (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
+// talk at Going Native 2013 for promoting value semantics via shared
+// pointers to immutable state.)
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// Forward declarations of classes; see below for proper documentation.
+class Elser;
+template <typename T>
+class Caser;
+namespace internal {
+class ResultExprImpl;
+class BoolExprImpl;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+extern template class SANDBOX_EXPORT
+ scoped_refptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+extern template class SANDBOX_EXPORT
+ scoped_refptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// ResultExpr is an opaque reference to an immutable result expression tree.
+typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
+
+// BoolExpr is an opaque reference to an immutable boolean expression tree.
+typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
+
+// Interface to implement to define a BPF sandbox policy.
+// TODO(mdempsky): "sandbox::bpf_dsl::SandboxBPFDSLPolicy" is
+// tediously repetitive; rename to just "Policy".
+class SANDBOX_EXPORT SandboxBPFDSLPolicy {
+ public:
+ SandboxBPFDSLPolicy() {}
+ virtual ~SandboxBPFDSLPolicy() {}
+
+ // User extension point for writing custom sandbox policies.
+ // The returned ResultExpr will control how the kernel responds to the
+ // specified system call number.
+ virtual ResultExpr EvaluateSyscall(int sysno) const = 0;
+
+ // Optional overload for specifying alternate behavior for invalid
+ // system calls. The default is to return ENOSYS.
+ virtual ResultExpr InvalidSyscall() const;
+
+ // Helper method so policies can just write Trap(func, aux).
+ static ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPFDSLPolicy);
+};
+
+// Allow specifies a result that the system call should be allowed to
+// execute normally.
+SANDBOX_EXPORT ResultExpr Allow();
+
+// Error specifies a result that the system call should fail with
+// error number |err|. As a special case, Error(0) will result in the
+// system call appearing to have succeeded, but without having any
+// side effects.
+SANDBOX_EXPORT ResultExpr Error(int err);
+
+// Kill specifies a result to kill the program and print an error message.
+SANDBOX_EXPORT ResultExpr Kill(const char* msg);
+
+// Trace specifies a result to notify a tracing process via the
+// PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call.
+// The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
+SANDBOX_EXPORT ResultExpr Trace(uint16_t aux);
+
+// Trap specifies a result that the system call should be handled by
+// trapping back into userspace and invoking |trap_func|, passing
+// |aux| as the second parameter.
+SANDBOX_EXPORT ResultExpr
+ Trap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// UnsafeTrap is like Trap, except the policy is marked as "unsafe"
+// and allowed to use SandboxSyscall to invoke any system call.
+//
+// NOTE: This feature, by definition, disables all security features of
+// the sandbox. It should never be used in production, but it can be
+// very useful to diagnose code that is incompatible with the sandbox.
+// If even a single system call returns "UnsafeTrap", the security of
+// entire sandbox should be considered compromised.
+SANDBOX_EXPORT ResultExpr
+ UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// BoolConst converts a bool value into a BoolExpr.
+SANDBOX_EXPORT BoolExpr BoolConst(bool value);
+
+// Various ways to combine boolean expressions into more complex expressions.
+// They follow standard boolean algebra laws.
+SANDBOX_EXPORT BoolExpr operator!(const BoolExpr& cond);
+SANDBOX_EXPORT BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs);
+SANDBOX_EXPORT BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs);
+
+template <typename T>
+class SANDBOX_EXPORT Arg {
+ public:
+ // Initializes the Arg to represent the |num|th system call
+ // argument (indexed from 0), which is of type |T|.
+ explicit Arg(int num);
+
+ Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
+
+ // Returns an Arg representing the current argument, but after
+ // bitwise-and'ing it with |rhs|.
+ friend Arg operator&(const Arg& lhs, uint64_t rhs) {
+ return Arg(lhs.num_, lhs.mask_ & rhs);
+ }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) equals |rhs|.
+ friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) does not equal |rhs|.
+ friend BoolExpr operator!=(const Arg& lhs, T rhs) { return !(lhs == rhs); }
+
+ private:
+ Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
+
+ BoolExpr EqualTo(T val) const;
+
+ int num_;
+ uint64_t mask_;
+
+ DISALLOW_ASSIGN(Arg);
+};
+
+// If begins a conditional result expression predicated on the
+// specified boolean expression.
+SANDBOX_EXPORT Elser If(const BoolExpr& cond, const ResultExpr& then_result);
+
+class SANDBOX_EXPORT Elser {
+ public:
+ Elser(const Elser& elser);
+ ~Elser();
+
+ // ElseIf extends the conditional result expression with another
+ // "if then" clause, predicated on the specified boolean expression.
+ Elser ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const;
+
+ // Else terminates a conditional result expression using |else_result| as
+ // the default fallback result expression.
+ ResultExpr Else(const ResultExpr& else_result) const;
+
+ private:
+ typedef std::pair<BoolExpr, ResultExpr> Clause;
+
+ explicit Elser(cons::List<Clause> clause_list);
+
+ cons::List<Clause> clause_list_;
+
+ friend Elser If(const BoolExpr&, const ResultExpr&);
+ template <typename T>
+ friend Caser<T> Switch(const Arg<T>&);
+ DISALLOW_ASSIGN(Elser);
+};
+
+// Switch begins a switch expression dispatched according to the
+// specified argument value.
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg);
+
+template <typename T>
+class SANDBOX_EXPORT Caser {
+ public:
+ Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {}
+ ~Caser() {}
+
+ // Case adds a single-value "case" clause to the switch.
+ Caser<T> Case(T value, ResultExpr result) const;
+
+ // Cases adds a multiple-value "case" clause to the switch.
+ // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
+ // of using this function.
+ Caser<T> Cases(const std::vector<T>& values, ResultExpr result) const;
+
+ // Terminate the switch with a "default" clause.
+ ResultExpr Default(ResultExpr result) const;
+
+ private:
+ Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {}
+
+ Arg<T> arg_;
+ Elser elser_;
+
+ template <typename U>
+ friend Caser<U> Switch(const Arg<U>&);
+ DISALLOW_ASSIGN(Caser);
+};
+
+// Recommended usage is to put
+// #define CASES SANDBOX_BPF_DSL_CASES
+// near the top of the .cc file (e.g., nearby any "using" statements), then
+// use like:
+// Switch(arg).CASES((3, 5, 7), result)...;
+#define SANDBOX_BPF_DSL_CASES(values, result) \
+ Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result)
+
+// Helper macro to construct a std::vector from an initializer list.
+// TODO(mdempsky): Convert to use C++11 initializer lists instead.
+#define SANDBOX_BPF_DSL_CASES_HELPER(value, ...) \
+ ({ \
+ const __typeof__(value) bpf_dsl_cases_values[] = {value, __VA_ARGS__}; \
+ std::vector<__typeof__(value)>( \
+ bpf_dsl_cases_values, \
+ bpf_dsl_cases_values + arraysize(bpf_dsl_cases_values)); \
+ })
+
+// =====================================================================
+// Official API ends here.
+// =====================================================================
+
+namespace internal {
+
+// Make argument-dependent lookup work. This is necessary because although
+// BoolExpr is defined in bpf_dsl, since it's merely a typedef for
+// scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
+// searches the "internal" nested namespace.
+using bpf_dsl::operator!;
+using bpf_dsl::operator||;
+using bpf_dsl::operator&&;
+
+// Returns a boolean expression that represents whether system call
+// argument |num| of size |size| is equal to |val|, when masked
+// according to |mask|. Users should use the Arg template class below
+// instead of using this API directly.
+SANDBOX_EXPORT BoolExpr
+ ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
+
+// Returns the default mask for a system call argument of the specified size.
+SANDBOX_EXPORT uint64_t DefaultMask(size_t size);
+
+} // namespace internal
+
+template <typename T>
+Arg<T>::Arg(int num)
+ : num_(num), mask_(internal::DefaultMask(sizeof(T))) {
+}
+
+// Definition requires ArgEq to have been declared. Moved out-of-line
+// to minimize how much internal clutter users have to ignore while
+// reading the header documentation.
+//
+// Additionally, we use this helper member function to avoid linker errors
+// caused by defining operator== out-of-line. For a more detailed explanation,
+// see http://www.parashift.com/c++-faq-lite/template-friends.html.
+template <typename T>
+BoolExpr Arg<T>::EqualTo(T val) const {
+ return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val));
+}
+
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
+ return Caser<T>(arg, Elser(nullptr));
+}
+
+template <typename T>
+Caser<T> Caser<T>::Case(T value, ResultExpr result) const {
+ return SANDBOX_BPF_DSL_CASES((value), result);
+}
+
+template <typename T>
+Caser<T> Caser<T>::Cases(const std::vector<T>& values,
+ ResultExpr result) const {
+ // Theoretically we could evaluate arg_ just once and emit a more efficient
+ // dispatch table, but for now we simply translate into an equivalent
+ // If/ElseIf/Else chain.
+
+ typedef typename std::vector<T>::const_iterator Iter;
+ BoolExpr test = BoolConst(false);
+ for (Iter i = values.begin(), end = values.end(); i != end; ++i) {
+ test = test || (arg_ == *i);
+ }
+ return Caser<T>(arg_, elser_.ElseIf(test, result));
+}
+
+template <typename T>
+ResultExpr Caser<T>::Default(ResultExpr result) const {
+ return elser_.Else(result);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
new file mode 100644
index 0000000..48b9169
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+class ErrorCode;
+
+namespace bpf_dsl {
+class PolicyCompiler;
+
+namespace internal {
+
+// Internal interface implemented by BoolExpr implementations.
+class BoolExprImpl : public base::RefCounted<BoolExprImpl> {
+ public:
+ // Compile uses |pc| to construct an ErrorCode that conditionally continues
+ // to either |true_ec| or |false_ec|, depending on whether the represented
+ // boolean expression is true or false.
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const = 0;
+
+ protected:
+ BoolExprImpl() {}
+ virtual ~BoolExprImpl() {}
+
+ private:
+ friend class base::RefCounted<BoolExprImpl>;
+ DISALLOW_COPY_AND_ASSIGN(BoolExprImpl);
+};
+
+// Internal interface implemented by ResultExpr implementations.
+class ResultExprImpl : public base::RefCounted<ResultExprImpl> {
+ public:
+ // Compile uses |pc| to construct an ErrorCode analogous to the represented
+ // result expression.
+ virtual ErrorCode Compile(PolicyCompiler* pc) const = 0;
+
+ // HasUnsafeTraps returns whether the result expression is or recursively
+ // contains an unsafe trap expression.
+ virtual bool HasUnsafeTraps() const;
+
+ protected:
+ ResultExprImpl() {}
+ virtual ~ResultExprImpl() {}
+
+ private:
+ friend class base::RefCounted<ResultExprImpl>;
+ DISALLOW_COPY_AND_ASSIGN(ResultExprImpl);
+};
+
+} // namespace internal
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc
new file mode 100644
index 0000000..90e83c6
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc
@@ -0,0 +1,2400 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#if defined(ANDROID)
+// Work-around for buggy headers in Android's NDK
+#define __user
+#endif
+#include <linux/futex.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/services/broker_process.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Workaround for Android's prctl.h file.
+#ifndef PR_GET_ENDIAN
+#define PR_GET_ENDIAN 19
+#endif
+#ifndef PR_CAPBSET_READ
+#define PR_CAPBSET_READ 23
+#define PR_CAPBSET_DROP 24
+#endif
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+const int kExpectedReturnValue = 42;
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// Set the global environment to allow the use of UnsafeTrap() policies.
+void EnableUnsafeTraps() {
+ // The use of UnsafeTrap() causes us to print a warning message. This is
+ // generally desirable, but it results in the unittest failing, as it doesn't
+ // expect any messages on "stderr". So, temporarily disable messages. The
+ // BPF_TEST() is guaranteed to turn messages back on, after the policy
+ // function has completed.
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+}
+
+// This test should execute no matter whether we have kernel support. So,
+// we make it a TEST() instead of a BPF_TEST().
+TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupports)) {
+ // We check that we don't crash, but it's ok if the kernel doesn't
+ // support it.
+ bool seccomp_bpf_supported =
+ SandboxBPF::SupportsSeccompSandbox(-1) == SandboxBPF::STATUS_AVAILABLE;
+ // We want to log whether or not seccomp BPF is actually supported
+ // since actual test coverage depends on it.
+ RecordProperty("SeccompBPFSupported",
+ seccomp_bpf_supported ? "true." : "false.");
+ std::cout << "Seccomp BPF supported: "
+ << (seccomp_bpf_supported ? "true." : "false.") << "\n";
+ RecordProperty("PointerSize", sizeof(void*));
+ std::cout << "Pointer size: " << sizeof(void*) << "\n";
+}
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupportsTwice)) {
+ SandboxBPF::SupportsSeccompSandbox(-1);
+ SandboxBPF::SupportsSeccompSandbox(-1);
+}
+
+// BPF_TEST does a lot of the boiler-plate code around setting up a
+// policy and optional passing data between the caller, the policy and
+// any Trap() handlers. This is great for writing short and concise tests,
+// and it helps us accidentally forgetting any of the crucial steps in
+// setting up the sandbox. But it wouldn't hurt to have at least one test
+// that explicitly walks through all these steps.
+
+intptr_t IncreaseCounter(const struct arch_seccomp_data& args, void* aux) {
+ BPF_ASSERT(aux);
+ int* counter = static_cast<int*>(aux);
+ return (*counter)++;
+}
+
+class VerboseAPITestingPolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit VerboseAPITestingPolicy(int* counter_ptr)
+ : counter_ptr_(counter_ptr) {}
+ virtual ~VerboseAPITestingPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return Trap(IncreaseCounter, counter_ptr_);
+ }
+ return Allow();
+ }
+
+ private:
+ int* counter_ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(VerboseAPITestingPolicy);
+};
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) {
+ if (SandboxBPF::SupportsSeccompSandbox(-1) ==
+ sandbox::SandboxBPF::STATUS_AVAILABLE) {
+ static int counter = 0;
+
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new VerboseAPITestingPolicy(&counter));
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ BPF_ASSERT_EQ(0, counter);
+ BPF_ASSERT_EQ(0, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(1, counter);
+ BPF_ASSERT_EQ(1, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(2, counter);
+ }
+}
+
+// A simple blacklist test
+
+class BlacklistNanosleepPolicy : public SandboxBPFDSLPolicy {
+ public:
+ BlacklistNanosleepPolicy() {}
+ virtual ~BlacklistNanosleepPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_nanosleep:
+ return Error(EACCES);
+ default:
+ return Allow();
+ }
+ }
+
+ static void AssertNanosleepFails() {
+ const struct timespec ts = {0, 0};
+ errno = 0;
+ BPF_ASSERT_EQ(-1, HANDLE_EINTR(syscall(__NR_nanosleep, &ts, NULL)));
+ BPF_ASSERT_EQ(EACCES, errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BlacklistNanosleepPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
+ BlacklistNanosleepPolicy::AssertNanosleepFails();
+}
+
+// Now do a simple whitelist test
+
+class WhitelistGetpidPolicy : public SandboxBPFDSLPolicy {
+ public:
+ WhitelistGetpidPolicy() {}
+ virtual ~WhitelistGetpidPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getpid:
+ case __NR_exit_group:
+ return Allow();
+ default:
+ return Error(ENOMEM);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WhitelistGetpidPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
+ // getpid() should be allowed
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getpid) > 0);
+ BPF_ASSERT(errno == 0);
+
+ // getpgid() should be denied
+ BPF_ASSERT(getpgid(0) == -1);
+ BPF_ASSERT(errno == ENOMEM);
+}
+
+// A simple blacklist policy, with a SIGSYS handler
+intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) {
+ // We also check that the auxiliary data is correct
+ SANDBOX_ASSERT(aux);
+ *(static_cast<int*>(aux)) = kExpectedReturnValue;
+ return -ENOMEM;
+}
+
+class BlacklistNanosleepTrapPolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit BlacklistNanosleepTrapPolicy(int* aux) : aux_(aux) {}
+ virtual ~BlacklistNanosleepTrapPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_nanosleep:
+ return Trap(EnomemHandler, aux_);
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ int* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlacklistNanosleepTrapPolicy);
+};
+
+BPF_TEST(SandboxBPF,
+ BasicBlacklistWithSigsys,
+ BlacklistNanosleepTrapPolicy,
+ int /* (*BPF_AUX) */) {
+ // getpid() should work properly
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getpid) > 0);
+ BPF_ASSERT(errno == 0);
+
+ // Our Auxiliary Data, should be reset by the signal handler
+ *BPF_AUX = -1;
+ const struct timespec ts = {0, 0};
+ BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
+ BPF_ASSERT(errno == ENOMEM);
+
+ // We expect the signal handler to modify AuxData
+ BPF_ASSERT(*BPF_AUX == kExpectedReturnValue);
+}
+
+// A simple test that verifies we can return arbitrary errno values.
+
+class ErrnoTestPolicy : public SandboxBPFDSLPolicy {
+ public:
+ ErrnoTestPolicy() {}
+ virtual ~ErrnoTestPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ErrnoTestPolicy);
+};
+
+ResultExpr ErrnoTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_dup3: // dup2 is a wrapper of dup3 in android
+#if defined(__NR_dup2)
+ case __NR_dup2:
+#endif
+ // Pretend that dup2() worked, but don't actually do anything.
+ return Error(0);
+ case __NR_setuid:
+#if defined(__NR_setuid32)
+ case __NR_setuid32:
+#endif
+ // Return errno = 1.
+ return Error(1);
+ case __NR_setgid:
+#if defined(__NR_setgid32)
+ case __NR_setgid32:
+#endif
+ // Return maximum errno value (typically 4095).
+ return Error(ErrorCode::ERR_MAX_ERRNO);
+ case __NR_uname:
+ // Return errno = 42;
+ return Error(42);
+ default:
+ return Allow();
+ }
+}
+
+BPF_TEST_C(SandboxBPF, ErrnoTest, ErrnoTestPolicy) {
+ // Verify that dup2() returns success, but doesn't actually run.
+ int fds[4];
+ BPF_ASSERT(pipe(fds) == 0);
+ BPF_ASSERT(pipe(fds + 2) == 0);
+ BPF_ASSERT(dup2(fds[2], fds[0]) == 0);
+ char buf[1] = {};
+ BPF_ASSERT(write(fds[1], "\x55", 1) == 1);
+ BPF_ASSERT(write(fds[3], "\xAA", 1) == 1);
+ BPF_ASSERT(read(fds[0], buf, 1) == 1);
+
+ // If dup2() executed, we will read \xAA, but it dup2() has been turned
+ // into a no-op by our policy, then we will read \x55.
+ BPF_ASSERT(buf[0] == '\x55');
+
+ // Verify that we can return the minimum and maximum errno values.
+ errno = 0;
+ BPF_ASSERT(setuid(0) == -1);
+ BPF_ASSERT(errno == 1);
+
+ // On Android, errno is only supported up to 255, otherwise errno
+ // processing is skipped.
+ // We work around this (crbug.com/181647).
+ if (sandbox::IsAndroid() && setgid(0) != -1) {
+ errno = 0;
+ BPF_ASSERT(setgid(0) == -ErrorCode::ERR_MAX_ERRNO);
+ BPF_ASSERT(errno == 0);
+ } else {
+ errno = 0;
+ BPF_ASSERT(setgid(0) == -1);
+ BPF_ASSERT(errno == ErrorCode::ERR_MAX_ERRNO);
+ }
+
+ // Finally, test an errno in between the minimum and maximum.
+ errno = 0;
+ struct utsname uts_buf;
+ BPF_ASSERT(uname(&uts_buf) == -1);
+ BPF_ASSERT(errno == 42);
+}
+
+// Testing the stacking of two sandboxes
+
+class StackingPolicyPartOne : public SandboxBPFDSLPolicy {
+ public:
+ StackingPolicyPartOne() {}
+ virtual ~StackingPolicyPartOne() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getppid: {
+ const Arg<int> arg(0);
+ return If(arg == 0, Allow()).Else(Error(EPERM));
+ }
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackingPolicyPartOne);
+};
+
+class StackingPolicyPartTwo : public SandboxBPFDSLPolicy {
+ public:
+ StackingPolicyPartTwo() {}
+ virtual ~StackingPolicyPartTwo() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getppid: {
+ const Arg<int> arg(0);
+ return If(arg == 0, Error(EINVAL)).Else(Allow());
+ }
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackingPolicyPartTwo);
+};
+
+BPF_TEST_C(SandboxBPF, StackingPolicy, StackingPolicyPartOne) {
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getppid, 0) > 0);
+ BPF_ASSERT(errno == 0);
+
+ BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ // Stack a second sandbox with its own policy. Verify that we can further
+ // restrict filters, but we cannot relax existing filters.
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new StackingPolicyPartTwo());
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getppid, 0) == -1);
+ BPF_ASSERT(errno == EINVAL);
+
+ BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+ BPF_ASSERT(errno == EPERM);
+}
+
+// A more complex, but synthetic policy. This tests the correctness of the BPF
+// program by iterating through all syscalls and checking for an errno that
+// depends on the syscall number. Unlike the Verifier, this exercises the BPF
+// interpreter in the kernel.
+
+// We try to make sure we exercise optimizations in the BPF compiler. We make
+// sure that the compiler can have an opportunity to coalesce syscalls with
+// contiguous numbers and we also make sure that disjoint sets can return the
+// same errno.
+int SysnoToRandomErrno(int sysno) {
+ // Small contiguous sets of 3 system calls return an errno equal to the
+ // index of that set + 1 (so that we never return a NUL errno).
+ return ((sysno & ~3) >> 2) % 29 + 1;
+}
+
+class SyntheticPolicy : public SandboxBPFDSLPolicy {
+ public:
+ SyntheticPolicy() {}
+ virtual ~SyntheticPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_exit_group || sysno == __NR_write) {
+ // exit_group() is special, we really need it to work.
+ // write() is needed for BPF_ASSERT() to report a useful error message.
+ return Allow();
+ }
+ return Error(SysnoToRandomErrno(sysno));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyntheticPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, SyntheticPolicy, SyntheticPolicy) {
+ // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
+ // overflow.
+ BPF_ASSERT(std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
+ static_cast<int>(MAX_PUBLIC_SYSCALL));
+
+ for (int syscall_number = static_cast<int>(MIN_SYSCALL);
+ syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
+ ++syscall_number) {
+ if (syscall_number == __NR_exit_group || syscall_number == __NR_write) {
+ // exit_group() is special
+ continue;
+ }
+ errno = 0;
+ BPF_ASSERT(syscall(syscall_number) == -1);
+ BPF_ASSERT(errno == SysnoToRandomErrno(syscall_number));
+ }
+}
+
+#if defined(__arm__)
+// A simple policy that tests whether ARM private system calls are supported
+// by our BPF compiler and by the BPF interpreter in the kernel.
+
+// For ARM private system calls, return an errno equal to their offset from
+// MIN_PRIVATE_SYSCALL plus 1 (to avoid NUL errno).
+int ArmPrivateSysnoToErrno(int sysno) {
+ if (sysno >= static_cast<int>(MIN_PRIVATE_SYSCALL) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return (sysno - MIN_PRIVATE_SYSCALL) + 1;
+ } else {
+ return ENOSYS;
+ }
+}
+
+class ArmPrivatePolicy : public SandboxBPFDSLPolicy {
+ public:
+ ArmPrivatePolicy() {}
+ virtual ~ArmPrivatePolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
+ // ARM private system calls.
+ if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return Error(ArmPrivateSysnoToErrno(sysno));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArmPrivatePolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ArmPrivatePolicy, ArmPrivatePolicy) {
+ for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
+ syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
+ ++syscall_number) {
+ errno = 0;
+ BPF_ASSERT(syscall(syscall_number) == -1);
+ BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number));
+ }
+}
+#endif // defined(__arm__)
+
+intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) {
+ // Count all invocations of our callback function.
+ ++*reinterpret_cast<int*>(aux);
+
+ // Verify that within the callback function all filtering is temporarily
+ // disabled.
+ BPF_ASSERT(syscall(__NR_getpid) > 1);
+
+ // Verify that we can now call the underlying system call without causing
+ // infinite recursion.
+ return SandboxBPF::ForwardSyscall(args);
+}
+
+class GreyListedPolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit GreyListedPolicy(int* aux) : aux_(aux) {
+ // Set the global environment for unsafe traps once.
+ EnableUnsafeTraps();
+ }
+ virtual ~GreyListedPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Some system calls must always be allowed, if our policy wants to make
+ // use of UnsafeTrap()
+ if (SandboxBPF::IsRequiredForUnsafeTrap(sysno)) {
+ return Allow();
+ } else if (sysno == __NR_getpid) {
+ // Disallow getpid()
+ return Error(EPERM);
+ } else {
+ // Allow (and count) all other system calls.
+ return UnsafeTrap(CountSyscalls, aux_);
+ }
+ }
+
+ private:
+ int* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(GreyListedPolicy);
+};
+
+BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* (*BPF_AUX) */) {
+ BPF_ASSERT(syscall(__NR_getpid) == -1);
+ BPF_ASSERT(errno == EPERM);
+ BPF_ASSERT(*BPF_AUX == 0);
+ BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid));
+ BPF_ASSERT(*BPF_AUX == 2);
+ char name[17] = {};
+ BPF_ASSERT(!syscall(__NR_prctl,
+ PR_GET_NAME,
+ name,
+ (void*)NULL,
+ (void*)NULL,
+ (void*)NULL));
+ BPF_ASSERT(*BPF_AUX == 3);
+ BPF_ASSERT(*name);
+}
+
+SANDBOX_TEST(SandboxBPF, EnableUnsafeTrapsInSigSysHandler) {
+ // Disabling warning messages that could confuse our test framework.
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ unsetenv(kSandboxDebuggingEnv);
+ SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == false);
+ setenv(kSandboxDebuggingEnv, "", 1);
+ SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == false);
+ setenv(kSandboxDebuggingEnv, "t", 1);
+ SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == true);
+}
+
+intptr_t PrctlHandler(const struct arch_seccomp_data& args, void*) {
+ if (args.args[0] == PR_CAPBSET_DROP && static_cast<int>(args.args[1]) == -1) {
+ // prctl(PR_CAPBSET_DROP, -1) is never valid. The kernel will always
+ // return an error. But our handler allows this call.
+ return 0;
+ } else {
+ return SandboxBPF::ForwardSyscall(args);
+ }
+}
+
+class PrctlPolicy : public SandboxBPFDSLPolicy {
+ public:
+ PrctlPolicy() {}
+ virtual ~PrctlPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ if (sysno == __NR_prctl) {
+ // Handle prctl() inside an UnsafeTrap()
+ return UnsafeTrap(PrctlHandler, NULL);
+ }
+
+ // Allow all other system calls.
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrctlPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ForwardSyscall, PrctlPolicy) {
+ // This call should never be allowed. But our policy will intercept it and
+ // let it pass successfully.
+ BPF_ASSERT(
+ !prctl(PR_CAPBSET_DROP, -1, (void*)NULL, (void*)NULL, (void*)NULL));
+
+ // Verify that the call will fail, if it makes it all the way to the kernel.
+ BPF_ASSERT(
+ prctl(PR_CAPBSET_DROP, -2, (void*)NULL, (void*)NULL, (void*)NULL) == -1);
+
+ // And verify that other uses of prctl() work just fine.
+ char name[17] = {};
+ BPF_ASSERT(!syscall(__NR_prctl,
+ PR_GET_NAME,
+ name,
+ (void*)NULL,
+ (void*)NULL,
+ (void*)NULL));
+ BPF_ASSERT(*name);
+
+ // Finally, verify that system calls other than prctl() are completely
+ // unaffected by our policy.
+ struct utsname uts = {};
+ BPF_ASSERT(!uname(&uts));
+ BPF_ASSERT(!strcmp(uts.sysname, "Linux"));
+}
+
+intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void*) {
+ return SandboxBPF::ForwardSyscall(args);
+}
+
+class RedirectAllSyscallsPolicy : public SandboxBPFDSLPolicy {
+ public:
+ RedirectAllSyscallsPolicy() {}
+ virtual ~RedirectAllSyscallsPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RedirectAllSyscallsPolicy);
+};
+
+ResultExpr RedirectAllSyscallsPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ // Some system calls must always be allowed, if our policy wants to make
+ // use of UnsafeTrap()
+ if (SandboxBPF::IsRequiredForUnsafeTrap(sysno))
+ return Allow();
+ return UnsafeTrap(AllowRedirectedSyscall, NULL);
+}
+
+int bus_handler_fd_ = -1;
+
+void SigBusHandler(int, siginfo_t* info, void* void_context) {
+ BPF_ASSERT(write(bus_handler_fd_, "\x55", 1) == 1);
+}
+
+BPF_TEST_C(SandboxBPF, SigBus, RedirectAllSyscallsPolicy) {
+ // We use the SIGBUS bit in the signal mask as a thread-local boolean
+ // value in the implementation of UnsafeTrap(). This is obviously a bit
+ // of a hack that could conceivably interfere with code that uses SIGBUS
+ // in more traditional ways. This test verifies that basic functionality
+ // of SIGBUS is not impacted, but it is certainly possibly to construe
+ // more complex uses of signals where our use of the SIGBUS mask is not
+ // 100% transparent. This is expected behavior.
+ int fds[2];
+ BPF_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
+ bus_handler_fd_ = fds[1];
+ struct sigaction sa = {};
+ sa.sa_sigaction = SigBusHandler;
+ sa.sa_flags = SA_SIGINFO;
+ BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0);
+ raise(SIGBUS);
+ char c = '\000';
+ BPF_ASSERT(read(fds[0], &c, 1) == 1);
+ BPF_ASSERT(close(fds[0]) == 0);
+ BPF_ASSERT(close(fds[1]) == 0);
+ BPF_ASSERT(c == 0x55);
+}
+
+BPF_TEST_C(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) {
+ // Signal masks are potentially tricky to handle. For instance, if we
+ // ever tried to update them from inside a Trap() or UnsafeTrap() handler,
+ // the call to sigreturn() at the end of the signal handler would undo
+ // all of our efforts. So, it makes sense to test that sigprocmask()
+ // works, even if we have a policy in place that makes use of UnsafeTrap().
+ // In practice, this works because we force sigprocmask() to be handled
+ // entirely in the kernel.
+ sigset_t mask0, mask1, mask2;
+
+ // Call sigprocmask() to verify that SIGUSR2 wasn't blocked, if we didn't
+ // change the mask (it shouldn't have been, as it isn't blocked by default
+ // in POSIX).
+ //
+ // Use SIGUSR2 because Android seems to use SIGUSR1 for some purpose.
+ sigemptyset(&mask0);
+ BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, &mask1));
+ BPF_ASSERT(!sigismember(&mask1, SIGUSR2));
+
+ // Try again, and this time we verify that we can block it. This
+ // requires a second call to sigprocmask().
+ sigaddset(&mask0, SIGUSR2);
+ BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL));
+ BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2));
+ BPF_ASSERT(sigismember(&mask2, SIGUSR2));
+}
+
+BPF_TEST_C(SandboxBPF, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
+ // An UnsafeTrap() (or for that matter, a Trap()) has to report error
+ // conditions by returning an exit code in the range -1..-4096. This
+ // should happen automatically if using ForwardSyscall(). If the TrapFnc()
+ // uses some other method to make system calls, then it is responsible
+ // for computing the correct return code.
+ // This test verifies that ForwardSyscall() does the correct thing.
+
+ // The glibc system wrapper will ultimately set errno for us. So, from normal
+ // userspace, all of this should be completely transparent.
+ errno = 0;
+ BPF_ASSERT(close(-1) == -1);
+ BPF_ASSERT(errno == EBADF);
+
+ // Explicitly avoid the glibc wrapper. This is not normally the way anybody
+ // would make system calls, but it allows us to verify that we don't
+ // accidentally mess with errno, when we shouldn't.
+ errno = 0;
+ struct arch_seccomp_data args = {};
+ args.nr = __NR_close;
+ args.args[0] = -1;
+ BPF_ASSERT(SandboxBPF::ForwardSyscall(args) == -EBADF);
+ BPF_ASSERT(errno == 0);
+}
+
+bool NoOpCallback() {
+ return true;
+}
+
+// Test a trap handler that makes use of a broker process to open().
+
+class InitializedOpenBroker {
+ public:
+ InitializedOpenBroker() : initialized_(false) {
+ std::vector<std::string> allowed_files;
+ allowed_files.push_back("/proc/allowed");
+ allowed_files.push_back("/proc/cpuinfo");
+
+ broker_process_.reset(
+ new BrokerProcess(EPERM, allowed_files, std::vector<std::string>()));
+ BPF_ASSERT(broker_process() != NULL);
+ BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback)));
+
+ initialized_ = true;
+ }
+ bool initialized() { return initialized_; }
+ class BrokerProcess* broker_process() { return broker_process_.get(); }
+
+ private:
+ bool initialized_;
+ scoped_ptr<class BrokerProcess> broker_process_;
+ DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
+};
+
+intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ BPF_ASSERT(aux);
+ BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
+ switch (args.nr) {
+ case __NR_faccessat: // access is a wrapper of faccessat in android
+ BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#if defined(__NR_access)
+ case __NR_access:
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+#if defined(__NR_open)
+ case __NR_open:
+ return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+ case __NR_openat:
+ // We only call open() so if we arrive here, it's because glibc uses
+ // the openat() system call.
+ BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
+ return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+ default:
+ BPF_ASSERT(false);
+ return -ENOSYS;
+ }
+}
+
+class DenyOpenPolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit DenyOpenPolicy(InitializedOpenBroker* iob) : iob_(iob) {}
+ virtual ~DenyOpenPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+
+ switch (sysno) {
+ case __NR_faccessat:
+#if defined(__NR_access)
+ case __NR_access:
+#endif
+#if defined(__NR_open)
+ case __NR_open:
+#endif
+ case __NR_openat:
+ // We get a InitializedOpenBroker class, but our trap handler wants
+ // the BrokerProcess object.
+ return Trap(BrokerOpenTrapHandler, iob_->broker_process());
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ InitializedOpenBroker* iob_;
+
+ DISALLOW_COPY_AND_ASSIGN(DenyOpenPolicy);
+};
+
+// We use a InitializedOpenBroker class, so that we can run unsandboxed
+// code in its constructor, which is the only way to do so in a BPF_TEST.
+BPF_TEST(SandboxBPF,
+ UseOpenBroker,
+ DenyOpenPolicy,
+ InitializedOpenBroker /* (*BPF_AUX) */) {
+ BPF_ASSERT(BPF_AUX->initialized());
+ BrokerProcess* broker_process = BPF_AUX->broker_process();
+ BPF_ASSERT(broker_process != NULL);
+
+ // First, use the broker "manually"
+ BPF_ASSERT(broker_process->Open("/proc/denied", O_RDONLY) == -EPERM);
+ BPF_ASSERT(broker_process->Access("/proc/denied", R_OK) == -EPERM);
+ BPF_ASSERT(broker_process->Open("/proc/allowed", O_RDONLY) == -ENOENT);
+ BPF_ASSERT(broker_process->Access("/proc/allowed", R_OK) == -ENOENT);
+
+ // Now use glibc's open() as an external library would.
+ BPF_ASSERT(open("/proc/denied", O_RDONLY) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ BPF_ASSERT(open("/proc/allowed", O_RDONLY) == -1);
+ BPF_ASSERT(errno == ENOENT);
+
+ // Also test glibc's openat(), some versions of libc use it transparently
+ // instead of open().
+ BPF_ASSERT(openat(AT_FDCWD, "/proc/denied", O_RDONLY) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ BPF_ASSERT(openat(AT_FDCWD, "/proc/allowed", O_RDONLY) == -1);
+ BPF_ASSERT(errno == ENOENT);
+
+ // And test glibc's access().
+ BPF_ASSERT(access("/proc/denied", R_OK) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ BPF_ASSERT(access("/proc/allowed", R_OK) == -1);
+ BPF_ASSERT(errno == ENOENT);
+
+ // This is also white listed and does exist.
+ int cpu_info_access = access("/proc/cpuinfo", R_OK);
+ BPF_ASSERT(cpu_info_access == 0);
+ int cpu_info_fd = open("/proc/cpuinfo", O_RDONLY);
+ BPF_ASSERT(cpu_info_fd >= 0);
+ char buf[1024];
+ BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0);
+}
+
+// Simple test demonstrating how to use SandboxBPF::Cond()
+
+class SimpleCondTestPolicy : public SandboxBPFDSLPolicy {
+ public:
+ SimpleCondTestPolicy() {}
+ virtual ~SimpleCondTestPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleCondTestPolicy);
+};
+
+ResultExpr SimpleCondTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+
+ // We deliberately return unusual errno values upon failure, so that we
+ // can uniquely test for these values. In a "real" policy, you would want
+ // to return more traditional values.
+ int flags_argument_position = -1;
+ switch (sysno) {
+#if defined(__NR_open)
+ case __NR_open:
+ flags_argument_position = 1;
+#endif
+ case __NR_openat: { // open can be a wrapper for openat(2).
+ if (sysno == __NR_openat)
+ flags_argument_position = 2;
+
+ // Allow opening files for reading, but don't allow writing.
+ COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
+ const Arg<int> flags(flags_argument_position);
+ return If((flags & O_ACCMODE) != 0, Error(EROFS)).Else(Allow());
+ }
+ case __NR_prctl: {
+ // Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
+ // disallow everything else.
+ const Arg<int> option(0);
+ return If(option == PR_SET_DUMPABLE || option == PR_GET_DUMPABLE, Allow())
+ .Else(Error(ENOMEM));
+ }
+ default:
+ return Allow();
+ }
+}
+
+BPF_TEST_C(SandboxBPF, SimpleCondTest, SimpleCondTestPolicy) {
+ int fd;
+ BPF_ASSERT((fd = open("/proc/self/comm", O_RDWR)) == -1);
+ BPF_ASSERT(errno == EROFS);
+ BPF_ASSERT((fd = open("/proc/self/comm", O_RDONLY)) >= 0);
+ close(fd);
+
+ int ret;
+ BPF_ASSERT((ret = prctl(PR_GET_DUMPABLE)) >= 0);
+ BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1 - ret) == 0);
+ BPF_ASSERT(prctl(PR_GET_ENDIAN, &ret) == -1);
+ BPF_ASSERT(errno == ENOMEM);
+}
+
+// This test exercises the SandboxBPF::Cond() method by building a complex
+// tree of conditional equality operations. It then makes system calls and
+// verifies that they return the values that we expected from our BPF
+// program.
+class EqualityStressTest {
+ public:
+ EqualityStressTest() {
+ // We want a deterministic test
+ srand(0);
+
+ // Iterates over system call numbers and builds a random tree of
+ // equality tests.
+ // We are actually constructing a graph of ArgValue objects. This
+ // graph will later be used to a) compute our sandbox policy, and
+ // b) drive the code that verifies the output from the BPF program.
+ COMPILE_ASSERT(
+ kNumTestCases < (int)(MAX_PUBLIC_SYSCALL - MIN_SYSCALL - 10),
+ num_test_cases_must_be_significantly_smaller_than_num_system_calls);
+ for (int sysno = MIN_SYSCALL, end = kNumTestCases; sysno < end; ++sysno) {
+ if (IsReservedSyscall(sysno)) {
+ // Skip reserved system calls. This ensures that our test frame
+ // work isn't impacted by the fact that we are overriding
+ // a lot of different system calls.
+ ++end;
+ arg_values_.push_back(NULL);
+ } else {
+ arg_values_.push_back(
+ RandomArgValue(rand() % kMaxArgs, 0, rand() % kMaxArgs));
+ }
+ }
+ }
+
+ ~EqualityStressTest() {
+ for (std::vector<ArgValue*>::iterator iter = arg_values_.begin();
+ iter != arg_values_.end();
+ ++iter) {
+ DeleteArgValue(*iter);
+ }
+ }
+
+ ResultExpr Policy(int sysno) {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno < 0 || sysno >= (int)arg_values_.size() ||
+ IsReservedSyscall(sysno)) {
+ // We only return ErrorCode values for the system calls that
+ // are part of our test data. Every other system call remains
+ // allowed.
+ return Allow();
+ } else {
+ // ToErrorCode() turns an ArgValue object into an ErrorCode that is
+ // suitable for use by a sandbox policy.
+ return ToErrorCode(arg_values_[sysno]);
+ }
+ }
+
+ void VerifyFilter() {
+ // Iterate over all system calls. Skip the system calls that have
+ // previously been determined as being reserved.
+ for (int sysno = 0; sysno < (int)arg_values_.size(); ++sysno) {
+ if (!arg_values_[sysno]) {
+ // Skip reserved system calls.
+ continue;
+ }
+ // Verify that system calls return the values that we expect them to
+ // return. This involves passing different combinations of system call
+ // parameters in order to exercise all possible code paths through the
+ // BPF filter program.
+ // We arbitrarily start by setting all six system call arguments to
+ // zero. And we then recursive traverse our tree of ArgValues to
+ // determine the necessary combinations of parameters.
+ intptr_t args[6] = {};
+ Verify(sysno, args, *arg_values_[sysno]);
+ }
+ }
+
+ private:
+ struct ArgValue {
+ int argno; // Argument number to inspect.
+ int size; // Number of test cases (must be > 0).
+ struct Tests {
+ uint32_t k_value; // Value to compare syscall arg against.
+ int err; // If non-zero, errno value to return.
+ struct ArgValue* arg_value; // Otherwise, more args needs inspecting.
+ }* tests;
+ int err; // If none of the tests passed, this is what
+ struct ArgValue* arg_value; // we'll return (this is the "else" branch).
+ };
+
+ bool IsReservedSyscall(int sysno) {
+ // There are a handful of system calls that we should never use in our
+ // test cases. These system calls are needed to allow the test framework
+ // to run properly.
+ // If we wanted to write fully generic code, there are more system calls
+ // that could be listed here, and it is quite difficult to come up with a
+ // truly comprehensive list. After all, we are deliberately making system
+ // calls unavailable. In practice, we have a pretty good idea of the system
+ // calls that will be made by this particular test. So, this small list is
+ // sufficient. But if anybody copy'n'pasted this code for other uses, they
+ // would have to review that the list.
+ return sysno == __NR_read || sysno == __NR_write || sysno == __NR_exit ||
+ sysno == __NR_exit_group || sysno == __NR_restart_syscall;
+ }
+
+ ArgValue* RandomArgValue(int argno, int args_mask, int remaining_args) {
+ // Create a new ArgValue and fill it with random data. We use as bit mask
+ // to keep track of the system call parameters that have previously been
+ // set; this ensures that we won't accidentally define a contradictory
+ // set of equality tests.
+ struct ArgValue* arg_value = new ArgValue();
+ args_mask |= 1 << argno;
+ arg_value->argno = argno;
+
+ // Apply some restrictions on just how complex our tests can be.
+ // Otherwise, we end up with a BPF program that is too complicated for
+ // the kernel to load.
+ int fan_out = kMaxFanOut;
+ if (remaining_args > 3) {
+ fan_out = 1;
+ } else if (remaining_args > 2) {
+ fan_out = 2;
+ }
+
+ // Create a couple of different test cases with randomized values that
+ // we want to use when comparing system call parameter number "argno".
+ arg_value->size = rand() % fan_out + 1;
+ arg_value->tests = new ArgValue::Tests[arg_value->size];
+
+ uint32_t k_value = rand();
+ for (int n = 0; n < arg_value->size; ++n) {
+ // Ensure that we have unique values
+ k_value += rand() % (RAND_MAX / (kMaxFanOut + 1)) + 1;
+
+ // There are two possible types of nodes. Either this is a leaf node;
+ // in that case, we have completed all the equality tests that we
+ // wanted to perform, and we can now compute a random "errno" value that
+ // we should return. Or this is part of a more complex boolean
+ // expression; in that case, we have to recursively add tests for some
+ // of system call parameters that we have not yet included in our
+ // tests.
+ arg_value->tests[n].k_value = k_value;
+ if (!remaining_args || (rand() & 1)) {
+ arg_value->tests[n].err = (rand() % 1000) + 1;
+ arg_value->tests[n].arg_value = NULL;
+ } else {
+ arg_value->tests[n].err = 0;
+ arg_value->tests[n].arg_value =
+ RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+ }
+ }
+ // Finally, we have to define what we should return if none of the
+ // previous equality tests pass. Again, we can either deal with a leaf
+ // node, or we can randomly add another couple of tests.
+ if (!remaining_args || (rand() & 1)) {
+ arg_value->err = (rand() % 1000) + 1;
+ arg_value->arg_value = NULL;
+ } else {
+ arg_value->err = 0;
+ arg_value->arg_value =
+ RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+ }
+ // We have now built a new (sub-)tree of ArgValues defining a set of
+ // boolean expressions for testing random system call arguments against
+ // random values. Return this tree to our caller.
+ return arg_value;
+ }
+
+ int RandomArg(int args_mask) {
+ // Compute a random system call parameter number.
+ int argno = rand() % kMaxArgs;
+
+ // Make sure that this same parameter number has not previously been
+ // used. Otherwise, we could end up with a test that is impossible to
+ // satisfy (e.g. args[0] == 1 && args[0] == 2).
+ while (args_mask & (1 << argno)) {
+ argno = (argno + 1) % kMaxArgs;
+ }
+ return argno;
+ }
+
+ void DeleteArgValue(ArgValue* arg_value) {
+ // Delete an ArgValue and all of its child nodes. This requires
+ // recursively descending into the tree.
+ if (arg_value) {
+ if (arg_value->size) {
+ for (int n = 0; n < arg_value->size; ++n) {
+ if (!arg_value->tests[n].err) {
+ DeleteArgValue(arg_value->tests[n].arg_value);
+ }
+ }
+ delete[] arg_value->tests;
+ }
+ if (!arg_value->err) {
+ DeleteArgValue(arg_value->arg_value);
+ }
+ delete arg_value;
+ }
+ }
+
+ ResultExpr ToErrorCode(ArgValue* arg_value) {
+ // Compute the ResultExpr that should be returned, if none of our
+ // tests succeed (i.e. the system call parameter doesn't match any
+ // of the values in arg_value->tests[].k_value).
+ ResultExpr err;
+ if (arg_value->err) {
+ // If this was a leaf node, return the errno value that we expect to
+ // return from the BPF filter program.
+ err = Error(arg_value->err);
+ } else {
+ // If this wasn't a leaf node yet, recursively descend into the rest
+ // of the tree. This will end up adding a few more SandboxBPF::Cond()
+ // tests to our ErrorCode.
+ err = ToErrorCode(arg_value->arg_value);
+ }
+
+ // Now, iterate over all the test cases that we want to compare against.
+ // This builds a chain of SandboxBPF::Cond() tests
+ // (aka "if ... elif ... elif ... elif ... fi")
+ for (int n = arg_value->size; n-- > 0;) {
+ ResultExpr matched;
+ // Again, we distinguish between leaf nodes and subtrees.
+ if (arg_value->tests[n].err) {
+ matched = Error(arg_value->tests[n].err);
+ } else {
+ matched = ToErrorCode(arg_value->tests[n].arg_value);
+ }
+ // For now, all of our tests are limited to 32bit.
+ // We have separate tests that check the behavior of 32bit vs. 64bit
+ // conditional expressions.
+ const Arg<uint32_t> arg(arg_value->argno);
+ err = If(arg == arg_value->tests[n].k_value, matched).Else(err);
+ }
+ return err;
+ }
+
+ void Verify(int sysno, intptr_t* args, const ArgValue& arg_value) {
+ uint32_t mismatched = 0;
+ // Iterate over all the k_values in arg_value.tests[] and verify that
+ // we see the expected return values from system calls, when we pass
+ // the k_value as a parameter in a system call.
+ for (int n = arg_value.size; n-- > 0;) {
+ mismatched += arg_value.tests[n].k_value;
+ args[arg_value.argno] = arg_value.tests[n].k_value;
+ if (arg_value.tests[n].err) {
+ VerifyErrno(sysno, args, arg_value.tests[n].err);
+ } else {
+ Verify(sysno, args, *arg_value.tests[n].arg_value);
+ }
+ }
+ // Find a k_value that doesn't match any of the k_values in
+ // arg_value.tests[]. In most cases, the current value of "mismatched"
+ // would fit this requirement. But on the off-chance that it happens
+ // to collide, we double-check.
+ try_again:
+ for (int n = arg_value.size; n-- > 0;) {
+ if (mismatched == arg_value.tests[n].k_value) {
+ ++mismatched;
+ goto try_again;
+ }
+ }
+ // Now verify that we see the expected return value from system calls,
+ // if we pass a value that doesn't match any of the conditions (i.e. this
+ // is testing the "else" clause of the conditions).
+ args[arg_value.argno] = mismatched;
+ if (arg_value.err) {
+ VerifyErrno(sysno, args, arg_value.err);
+ } else {
+ Verify(sysno, args, *arg_value.arg_value);
+ }
+ // Reset args[arg_value.argno]. This is not technically needed, but it
+ // makes it easier to reason about the correctness of our tests.
+ args[arg_value.argno] = 0;
+ }
+
+ void VerifyErrno(int sysno, intptr_t* args, int err) {
+ // We installed BPF filters that return different errno values
+ // based on the system call number and the parameters that we decided
+ // to pass in. Verify that this condition holds true.
+ BPF_ASSERT(
+ Syscall::Call(
+ sysno, args[0], args[1], args[2], args[3], args[4], args[5]) ==
+ -err);
+ }
+
+ // Vector of ArgValue trees. These trees define all the possible boolean
+ // expressions that we want to turn into a BPF filter program.
+ std::vector<ArgValue*> arg_values_;
+
+ // Don't increase these values. We are pushing the limits of the maximum
+ // BPF program that the kernel will allow us to load. If the values are
+ // increased too much, the test will start failing.
+#if defined(__aarch64__)
+ static const int kNumTestCases = 30;
+#else
+ static const int kNumTestCases = 40;
+#endif
+ static const int kMaxFanOut = 3;
+ static const int kMaxArgs = 6;
+};
+
+class EqualityStressTestPolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit EqualityStressTestPolicy(EqualityStressTest* aux) : aux_(aux) {}
+ virtual ~EqualityStressTestPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ return aux_->Policy(sysno);
+ }
+
+ private:
+ EqualityStressTest* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(EqualityStressTestPolicy);
+};
+
+BPF_TEST(SandboxBPF,
+ EqualityTests,
+ EqualityStressTestPolicy,
+ EqualityStressTest /* (*BPF_AUX) */) {
+ BPF_AUX->VerifyFilter();
+}
+
+class EqualityArgumentWidthPolicy : public SandboxBPFDSLPolicy {
+ public:
+ EqualityArgumentWidthPolicy() {}
+ virtual ~EqualityArgumentWidthPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EqualityArgumentWidthPolicy);
+};
+
+ResultExpr EqualityArgumentWidthPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ const Arg<uint32_t> arg32(1);
+ const Arg<uint64_t> arg64(1);
+ return Switch(option)
+ .Case(0, If(arg32 == 0x55555555, Error(1)).Else(Error(2)))
+#if __SIZEOF_POINTER__ > 4
+ .Case(1, If(arg64 == 0x55555555AAAAAAAAULL, Error(1)).Else(Error(2)))
+#endif
+ .Default(Error(3));
+ }
+ return Allow();
+}
+
+BPF_TEST_C(SandboxBPF, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0, 0x55555555) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0, 0xAAAAAAAA) == -2);
+#if __SIZEOF_POINTER__ > 4
+ // On 32bit machines, there is no way to pass a 64bit argument through the
+ // syscall interface. So, we have to skip the part of the test that requires
+ // 64bit arguments.
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x55555555AAAAAAAAULL) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x5555555500000000ULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x5555555511111111ULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x11111111AAAAAAAAULL) == -2);
+#endif
+}
+
+#if __SIZEOF_POINTER__ > 4
+// On 32bit machines, there is no way to pass a 64bit argument through the
+// syscall interface. So, we have to skip the part of the test that requires
+// 64bit arguments.
+BPF_DEATH_TEST_C(SandboxBPF,
+ EqualityArgumentUnallowed64bit,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityArgumentWidthPolicy) {
+ Syscall::Call(__NR_uname, 0, 0x5555555555555555ULL);
+}
+#endif
+
+class EqualityWithNegativeArgumentsPolicy : public SandboxBPFDSLPolicy {
+ public:
+ EqualityWithNegativeArgumentsPolicy() {}
+ virtual ~EqualityWithNegativeArgumentsPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ // TODO(mdempsky): This currently can't be Arg<int> because then
+ // 0xFFFFFFFF will be treated as a (signed) int, and then when
+ // Arg::EqualTo casts it to uint64_t, it will be sign extended.
+ const Arg<unsigned> arg(0);
+ return If(arg == 0xFFFFFFFF, Error(1)).Else(Error(2));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EqualityWithNegativeArgumentsPolicy);
+};
+
+BPF_TEST_C(SandboxBPF,
+ EqualityWithNegativeArguments,
+ EqualityWithNegativeArgumentsPolicy) {
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0xFFFFFFFF) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, -1) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, -1LL) == -1);
+}
+
+#if __SIZEOF_POINTER__ > 4
+BPF_DEATH_TEST_C(SandboxBPF,
+ EqualityWithNegative64bitArguments,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityWithNegativeArgumentsPolicy) {
+ // When expecting a 32bit system call argument, we look at the MSB of the
+ // 64bit value and allow both "0" and "-1". But the latter is allowed only
+ // iff the LSB was negative. So, this death test should error out.
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
+}
+#endif
+
+class AllBitTestPolicy : public SandboxBPFDSLPolicy {
+ public:
+ AllBitTestPolicy() {}
+ virtual ~AllBitTestPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static ResultExpr HasAllBits32(uint32_t bits);
+ static ResultExpr HasAllBits64(uint64_t bits);
+
+ DISALLOW_COPY_AND_ASSIGN(AllBitTestPolicy);
+};
+
+ResultExpr AllBitTestPolicy::HasAllBits32(uint32_t bits) {
+ if (bits == 0) {
+ return Error(1);
+ }
+ const Arg<uint32_t> arg(1);
+ return If((arg & bits) == bits, Error(1)).Else(Error(0));
+}
+
+ResultExpr AllBitTestPolicy::HasAllBits64(uint64_t bits) {
+ if (bits == 0) {
+ return Error(1);
+ }
+ const Arg<uint64_t> arg(1);
+ return If((arg & bits) == bits, Error(1)).Else(Error(0));
+}
+
+ResultExpr AllBitTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Test masked-equality cases that should trigger the "has all bits"
+ // peephole optimizations. We try to find bitmasks that could conceivably
+ // touch corner cases.
+ // For all of these tests, we override the uname(). We can make use with
+ // a single system call number, as we use the first system call argument to
+ // select the different bit masks that we want to test against.
+ if (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ return Switch(option)
+ .Case(0, HasAllBits32(0x0))
+ .Case(1, HasAllBits32(0x1))
+ .Case(2, HasAllBits32(0x3))
+ .Case(3, HasAllBits32(0x80000000))
+#if __SIZEOF_POINTER__ > 4
+ .Case(4, HasAllBits64(0x0))
+ .Case(5, HasAllBits64(0x1))
+ .Case(6, HasAllBits64(0x3))
+ .Case(7, HasAllBits64(0x80000000))
+ .Case(8, HasAllBits64(0x100000000ULL))
+ .Case(9, HasAllBits64(0x300000000ULL))
+ .Case(10, HasAllBits64(0x100000001ULL))
+#endif
+ .Default(Kill("Invalid test case number"));
+ }
+ return Allow();
+}
+
+// Define a macro that performs tests using our test policy.
+// NOTE: Not all of the arguments in this macro are actually used!
+// They are here just to serve as documentation of the conditions
+// implemented in the test policy.
+// Most notably, "op" and "mask" are unused by the macro. If you want
+// to make changes to these values, you will have to edit the
+// test policy instead.
+#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \
+ BPF_ASSERT(Syscall::Call(__NR_uname, (testcase), (arg)) == (expected_value))
+
+// Our uname() system call returns ErrorCode(1) for success and
+// ErrorCode(0) for failure. Syscall::Call() turns this into an
+// exit code of -1 or 0.
+#define EXPECT_FAILURE 0
+#define EXPECT_SUCCESS -1
+
+// A couple of our tests behave differently on 32bit and 64bit systems, as
+// there is no way for a 32bit system call to pass in a 64bit system call
+// argument "arg".
+// We expect these tests to succeed on 64bit systems, but to tail on 32bit
+// systems.
+#define EXPT64_SUCCESS (sizeof(void*) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE)
+BPF_TEST_C(SandboxBPF, AllBitTests, AllBitTestPolicy) {
+ // 32bit test: all of 0x0 (should always be true)
+ BITMASK_TEST( 0, 0, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, 1, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, 3, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, 0xFFFFFFFFU, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, -1LL, ALLBITS32, 0, EXPECT_SUCCESS);
+
+ // 32bit test: all of 0x1
+ BITMASK_TEST( 1, 0, ALLBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 1, ALLBITS32, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 1, 2, ALLBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 3, ALLBITS32, 0x1, EXPECT_SUCCESS);
+
+ // 32bit test: all of 0x3
+ BITMASK_TEST( 2, 0, ALLBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 1, ALLBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 2, ALLBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 3, ALLBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 7, ALLBITS32, 0x3, EXPECT_SUCCESS);
+
+ // 32bit test: all of 0x80000000
+ BITMASK_TEST( 3, 0, ALLBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x40000000U, ALLBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x80000000U, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, 0xC0000000U, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, -0x80000000LL, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+
+#if __SIZEOF_POINTER__ > 4
+ // 64bit test: all of 0x0 (should always be true)
+ BITMASK_TEST( 4, 0, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 1, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 3, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 0xFFFFFFFFU, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 0x100000000LL, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 0x300000000LL, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4,0x8000000000000000LL, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, -1LL, ALLBITS64, 0, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x1
+ BITMASK_TEST( 5, 0, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 1, ALLBITS64, 1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 2, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 3, ALLBITS64, 1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000000LL, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000001LL, ALLBITS64, 1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000002LL, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000003LL, ALLBITS64, 1, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x3
+ BITMASK_TEST( 6, 0, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 1, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 2, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 3, ALLBITS64, 3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 7, ALLBITS64, 3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000000LL, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000001LL, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000002LL, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000003LL, ALLBITS64, 3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000007LL, ALLBITS64, 3, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x80000000
+ BITMASK_TEST( 7, 0, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x40000000U, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x80000000U, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0xC0000000U, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x80000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x100000000LL, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x140000000LL, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x180000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x1C0000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x180000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x100000000
+ BITMASK_TEST( 8, 0x000000000LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000000LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000000LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000000LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x000000001LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000001LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000001LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000001LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+
+ // 64bit test: all of 0x300000000
+ BITMASK_TEST( 9, 0x000000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x200000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x300000000LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000000LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x000000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x200000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x300000001LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000001LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+
+ // 64bit test: all of 0x100000001
+ BITMASK_TEST(10, 0x000000000LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, 0x000000001LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, 0x100000000LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, 0x100000001LL, ALLBITS64,0x100000001, EXPT64_SUCCESS);
+ BITMASK_TEST(10, 0xFFFFFFFFU, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS);
+#endif
+}
+
+class AnyBitTestPolicy : public SandboxBPFDSLPolicy {
+ public:
+ AnyBitTestPolicy() {}
+ virtual ~AnyBitTestPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static ResultExpr HasAnyBits32(uint32_t);
+ static ResultExpr HasAnyBits64(uint64_t);
+
+ DISALLOW_COPY_AND_ASSIGN(AnyBitTestPolicy);
+};
+
+ResultExpr AnyBitTestPolicy::HasAnyBits32(uint32_t bits) {
+ if (bits == 0) {
+ return Error(0);
+ }
+ const Arg<uint32_t> arg(1);
+ return If((arg & bits) != 0, Error(1)).Else(Error(0));
+}
+
+ResultExpr AnyBitTestPolicy::HasAnyBits64(uint64_t bits) {
+ if (bits == 0) {
+ return Error(0);
+ }
+ const Arg<uint64_t> arg(1);
+ return If((arg & bits) != 0, Error(1)).Else(Error(0));
+}
+
+ResultExpr AnyBitTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Test masked-equality cases that should trigger the "has any bits"
+ // peephole optimizations. We try to find bitmasks that could conceivably
+ // touch corner cases.
+ // For all of these tests, we override the uname(). We can make use with
+ // a single system call number, as we use the first system call argument to
+ // select the different bit masks that we want to test against.
+ if (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ return Switch(option)
+ .Case(0, HasAnyBits32(0x0))
+ .Case(1, HasAnyBits32(0x1))
+ .Case(2, HasAnyBits32(0x3))
+ .Case(3, HasAnyBits32(0x80000000))
+#if __SIZEOF_POINTER__ > 4
+ .Case(4, HasAnyBits64(0x0))
+ .Case(5, HasAnyBits64(0x1))
+ .Case(6, HasAnyBits64(0x3))
+ .Case(7, HasAnyBits64(0x80000000))
+ .Case(8, HasAnyBits64(0x100000000ULL))
+ .Case(9, HasAnyBits64(0x300000000ULL))
+ .Case(10, HasAnyBits64(0x100000001ULL))
+#endif
+ .Default(Kill("Invalid test case number"));
+ }
+ return Allow();
+}
+
+BPF_TEST_C(SandboxBPF, AnyBitTests, AnyBitTestPolicy) {
+ // 32bit test: any of 0x0 (should always be false)
+ BITMASK_TEST( 0, 0, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 1, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 3, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 0xFFFFFFFFU, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, -1LL, ANYBITS32, 0x0, EXPECT_FAILURE);
+
+ // 32bit test: any of 0x1
+ BITMASK_TEST( 1, 0, ANYBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 1, ANYBITS32, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 1, 2, ANYBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 3, ANYBITS32, 0x1, EXPECT_SUCCESS);
+
+ // 32bit test: any of 0x3
+ BITMASK_TEST( 2, 0, ANYBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 1, ANYBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 2, ANYBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 3, ANYBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 7, ANYBITS32, 0x3, EXPECT_SUCCESS);
+
+ // 32bit test: any of 0x80000000
+ BITMASK_TEST( 3, 0, ANYBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x40000000U, ANYBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x80000000U, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, 0xC0000000U, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, -0x80000000LL, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+
+#if __SIZEOF_POINTER__ > 4
+ // 64bit test: any of 0x0 (should always be false)
+ BITMASK_TEST( 4, 0, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 1, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 3, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 0xFFFFFFFFU, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 0x100000000LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 0x300000000LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4,0x8000000000000000LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, -1LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+
+ // 64bit test: any of 0x1
+ BITMASK_TEST( 5, 0, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 1, ANYBITS64, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 2, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 3, ANYBITS64, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000001LL, ANYBITS64, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000000LL, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000002LL, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000003LL, ANYBITS64, 0x1, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x3
+ BITMASK_TEST( 6, 0, ANYBITS64, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 1, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 2, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 3, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 7, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000000LL, ANYBITS64, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000001LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000002LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000003LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000007LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x80000000
+ BITMASK_TEST( 7, 0, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x40000000U, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x80000000U, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0xC0000000U, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x80000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x100000000LL, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x140000000LL, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x180000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x1C0000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x180000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x100000000
+ BITMASK_TEST( 8, 0x000000000LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000000LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000000LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000000LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x000000001LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000001LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000001LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000001LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+
+ // 64bit test: any of 0x300000000
+ BITMASK_TEST( 9, 0x000000000LL, ANYBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x200000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x300000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x000000001LL, ANYBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x200000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x300000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+
+ // 64bit test: any of 0x100000001
+ BITMASK_TEST( 10, 0x000000000LL, ANYBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST( 10, 0x000000001LL, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+ BITMASK_TEST( 10, 0x100000000LL, ANYBITS64,0x100000001, EXPT64_SUCCESS);
+ BITMASK_TEST( 10, 0x100000001LL, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+ BITMASK_TEST( 10, 0xFFFFFFFFU, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+ BITMASK_TEST( 10, -1L, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+#endif
+}
+
+class MaskedEqualTestPolicy : public SandboxBPFDSLPolicy {
+ public:
+ MaskedEqualTestPolicy() {}
+ virtual ~MaskedEqualTestPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static ResultExpr MaskedEqual32(uint32_t mask, uint32_t value);
+ static ResultExpr MaskedEqual64(uint64_t mask, uint64_t value);
+
+ DISALLOW_COPY_AND_ASSIGN(MaskedEqualTestPolicy);
+};
+
+ResultExpr MaskedEqualTestPolicy::MaskedEqual32(uint32_t mask, uint32_t value) {
+ const Arg<uint32_t> arg(1);
+ return If((arg & mask) == value, Error(1)).Else(Error(0));
+}
+
+ResultExpr MaskedEqualTestPolicy::MaskedEqual64(uint64_t mask, uint64_t value) {
+ const Arg<uint64_t> arg(1);
+ return If((arg & mask) == value, Error(1)).Else(Error(0));
+}
+
+ResultExpr MaskedEqualTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+
+ if (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ return Switch(option)
+ .Case(0, MaskedEqual32(0x00ff00ff, 0x005500aa))
+#if __SIZEOF_POINTER__ > 4
+ .Case(1, MaskedEqual64(0x00ff00ff00000000, 0x005500aa00000000))
+ .Case(2, MaskedEqual64(0x00ff00ff00ff00ff, 0x005500aa005500aa))
+#endif
+ .Default(Kill("Invalid test case number"));
+ }
+
+ return Allow();
+}
+
+#define MASKEQ_TEST(rulenum, arg, expected_result) \
+ BPF_ASSERT(Syscall::Call(__NR_uname, (rulenum), (arg)) == (expected_result))
+
+BPF_TEST_C(SandboxBPF, MaskedEqualTests, MaskedEqualTestPolicy) {
+ // Allowed: 0x__55__aa
+ MASKEQ_TEST(0, 0x00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000001, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000003, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000100, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000300, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x005500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0x005500ab, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x005600aa, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x005501aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0x005503aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0x555500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0xaa5500aa, EXPECT_SUCCESS);
+
+#if __SIZEOF_POINTER__ > 4
+ // Allowed: 0x__55__aa________
+ MASKEQ_TEST(1, 0x0000000000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000000000010, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000000000050, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000100000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000300000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000010000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000030000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x005500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0x005500ab00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x005600aa00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x005501aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0x005503aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0x555500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0xaa5500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0xaa5500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0xaa5500aa0000cafe, EXPECT_SUCCESS);
+
+ // Allowed: 0x__55__aa__55__aa
+ MASKEQ_TEST(2, 0x0000000000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000000000010, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000000000050, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000100000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000300000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000010000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000030000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x00000000005500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005500aa00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005500aa005500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(2, 0x005500aa005700aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005700aa005500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005500aa004500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x004500aa005500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005512aa005500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(2, 0x005500aa005534aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(2, 0xff5500aa0055ffaa, EXPECT_SUCCESS);
+#endif
+}
+
+intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void* aux) {
+ if (args.args[0] != (CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD)) {
+ // We expect to get called for an attempt to fork(). No need to log that
+ // call. But if we ever get called for anything else, we want to verbosely
+ // print as much information as possible.
+ const char* msg = (const char*)aux;
+ printf(
+ "Clone() was called with unexpected arguments\n"
+ " nr: %d\n"
+ " 1: 0x%llX\n"
+ " 2: 0x%llX\n"
+ " 3: 0x%llX\n"
+ " 4: 0x%llX\n"
+ " 5: 0x%llX\n"
+ " 6: 0x%llX\n"
+ "%s\n",
+ args.nr,
+ (long long)args.args[0],
+ (long long)args.args[1],
+ (long long)args.args[2],
+ (long long)args.args[3],
+ (long long)args.args[4],
+ (long long)args.args[5],
+ msg);
+ }
+ return -EPERM;
+}
+
+class PthreadPolicyEquality : public SandboxBPFDSLPolicy {
+ public:
+ PthreadPolicyEquality() {}
+ virtual ~PthreadPolicyEquality() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PthreadPolicyEquality);
+};
+
+ResultExpr PthreadPolicyEquality::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // This policy allows creating threads with pthread_create(). But it
+ // doesn't allow any other uses of clone(). Most notably, it does not
+ // allow callers to implement fork() or vfork() by passing suitable flags
+ // to the clone() system call.
+ if (sysno == __NR_clone) {
+ // We have seen two different valid combinations of flags. Glibc
+ // uses the more modern flags, sets the TLS from the call to clone(), and
+ // uses futexes to monitor threads. Android's C run-time library, doesn't
+ // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED.
+ // More recent versions of Android don't set CLONE_DETACHED anymore, so
+ // the last case accounts for that.
+ // The following policy is very strict. It only allows the exact masks
+ // that we have seen in known implementations. It is probably somewhat
+ // stricter than what we would want to do.
+ const uint64_t kGlibcCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS |
+ CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const uint64_t kBaseAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const Arg<unsigned long> flags(0);
+ return If(flags == kGlibcCloneMask ||
+ flags == (kBaseAndroidCloneMask | CLONE_DETACHED) ||
+ flags == kBaseAndroidCloneMask,
+ Allow()).Else(Trap(PthreadTrapHandler, "Unknown mask"));
+ }
+
+ return Allow();
+}
+
+class PthreadPolicyBitMask : public SandboxBPFDSLPolicy {
+ public:
+ PthreadPolicyBitMask() {}
+ virtual ~PthreadPolicyBitMask() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static BoolExpr HasAnyBits(const Arg<unsigned long>& arg, unsigned long bits);
+ static BoolExpr HasAllBits(const Arg<unsigned long>& arg, unsigned long bits);
+
+ DISALLOW_COPY_AND_ASSIGN(PthreadPolicyBitMask);
+};
+
+BoolExpr PthreadPolicyBitMask::HasAnyBits(const Arg<unsigned long>& arg,
+ unsigned long bits) {
+ return (arg & bits) != 0;
+}
+
+BoolExpr PthreadPolicyBitMask::HasAllBits(const Arg<unsigned long>& arg,
+ unsigned long bits) {
+ return (arg & bits) == bits;
+}
+
+ResultExpr PthreadPolicyBitMask::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // This policy allows creating threads with pthread_create(). But it
+ // doesn't allow any other uses of clone(). Most notably, it does not
+ // allow callers to implement fork() or vfork() by passing suitable flags
+ // to the clone() system call.
+ if (sysno == __NR_clone) {
+ // We have seen two different valid combinations of flags. Glibc
+ // uses the more modern flags, sets the TLS from the call to clone(), and
+ // uses futexes to monitor threads. Android's C run-time library, doesn't
+ // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED.
+ // The following policy allows for either combination of flags, but it
+ // is generally a little more conservative than strictly necessary. We
+ // err on the side of rather safe than sorry.
+ // Very noticeably though, we disallow fork() (which is often just a
+ // wrapper around clone()).
+ const unsigned long kMandatoryFlags = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const unsigned long kFutexFlags =
+ CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const unsigned long kNoopFlags = CLONE_DETACHED;
+ const unsigned long kKnownFlags =
+ kMandatoryFlags | kFutexFlags | kNoopFlags;
+
+ const Arg<unsigned long> flags(0);
+ return If(HasAnyBits(flags, ~kKnownFlags),
+ Trap(PthreadTrapHandler, "Unexpected CLONE_XXX flag found"))
+ .ElseIf(!HasAllBits(flags, kMandatoryFlags),
+ Trap(PthreadTrapHandler,
+ "Missing mandatory CLONE_XXX flags "
+ "when creating new thread"))
+ .ElseIf(
+ !HasAllBits(flags, kFutexFlags) && HasAnyBits(flags, kFutexFlags),
+ Trap(PthreadTrapHandler,
+ "Must set either all or none of the TLS and futex bits in "
+ "call to clone()"))
+ .Else(Allow());
+ }
+
+ return Allow();
+}
+
+static void* ThreadFnc(void* arg) {
+ ++*reinterpret_cast<int*>(arg);
+ Syscall::Call(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0);
+ return NULL;
+}
+
+static void PthreadTest() {
+ // Attempt to start a joinable thread. This should succeed.
+ pthread_t thread;
+ int thread_ran = 0;
+ BPF_ASSERT(!pthread_create(&thread, NULL, ThreadFnc, &thread_ran));
+ BPF_ASSERT(!pthread_join(thread, NULL));
+ BPF_ASSERT(thread_ran);
+
+ // Attempt to start a detached thread. This should succeed.
+ thread_ran = 0;
+ pthread_attr_t attr;
+ BPF_ASSERT(!pthread_attr_init(&attr));
+ BPF_ASSERT(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
+ BPF_ASSERT(!pthread_create(&thread, &attr, ThreadFnc, &thread_ran));
+ BPF_ASSERT(!pthread_attr_destroy(&attr));
+ while (Syscall::Call(__NR_futex, &thread_ran, FUTEX_WAIT, 0, 0, 0, 0) ==
+ -EINTR) {
+ }
+ BPF_ASSERT(thread_ran);
+
+ // Attempt to fork() a process using clone(). This should fail. We use the
+ // same flags that glibc uses when calling fork(). But we don't actually
+ // try calling the fork() implementation in the C run-time library, as
+ // run-time libraries other than glibc might call __NR_fork instead of
+ // __NR_clone, and that would introduce a bogus test failure.
+ int pid;
+ BPF_ASSERT(Syscall::Call(__NR_clone,
+ CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD,
+ 0,
+ 0,
+ &pid) == -EPERM);
+}
+
+BPF_TEST_C(SandboxBPF, PthreadEquality, PthreadPolicyEquality) {
+ PthreadTest();
+}
+
+BPF_TEST_C(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) {
+ PthreadTest();
+}
+
+// libc might not define these even though the kernel supports it.
+#ifndef PTRACE_O_TRACESECCOMP
+#define PTRACE_O_TRACESECCOMP 0x00000080
+#endif
+
+#ifdef PTRACE_EVENT_SECCOMP
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP)
+#else
+// When Debian/Ubuntu backported seccomp-bpf support into earlier kernels, they
+// changed the value of PTRACE_EVENT_SECCOMP from 7 to 8, since 7 was taken by
+// PTRACE_EVENT_STOP (upstream chose to renumber PTRACE_EVENT_STOP to 128). If
+// PTRACE_EVENT_SECCOMP isn't defined, we have no choice but to consider both
+// values here.
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == 7 || (status >> 16) == 8)
+#endif
+
+#if defined(__arm__)
+#ifndef PTRACE_SET_SYSCALL
+#define PTRACE_SET_SYSCALL 23
+#endif
+#endif
+
+#if defined(__aarch64__)
+#ifndef PTRACE_GETREGS
+#define PTRACE_GETREGS 12
+#endif
+#endif
+
+#if defined(__aarch64__)
+#ifndef PTRACE_SETREGS
+#define PTRACE_SETREGS 13
+#endif
+#endif
+
+// Changes the syscall to run for a child being sandboxed using seccomp-bpf with
+// PTRACE_O_TRACESECCOMP. Should only be called when the child is stopped on
+// PTRACE_EVENT_SECCOMP.
+//
+// regs should contain the current set of registers of the child, obtained using
+// PTRACE_GETREGS.
+//
+// Depending on the architecture, this may modify regs, so the caller is
+// responsible for committing these changes using PTRACE_SETREGS.
+long SetSyscall(pid_t pid, regs_struct* regs, int syscall_number) {
+#if defined(__arm__)
+ // On ARM, the syscall is changed using PTRACE_SET_SYSCALL. We cannot use the
+ // libc ptrace call as the request parameter is an enum, and
+ // PTRACE_SET_SYSCALL may not be in the enum.
+ return syscall(__NR_ptrace, PTRACE_SET_SYSCALL, pid, NULL, syscall_number);
+#endif
+
+ SECCOMP_PT_SYSCALL(*regs) = syscall_number;
+ return 0;
+}
+
+const uint16_t kTraceData = 0xcc;
+
+class TraceAllPolicy : public SandboxBPFDSLPolicy {
+ public:
+ TraceAllPolicy() {}
+ virtual ~TraceAllPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int system_call_number) const override {
+ return Trace(kTraceData);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TraceAllPolicy);
+};
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) {
+ if (SandboxBPF::SupportsSeccompSandbox(-1) !=
+ sandbox::SandboxBPF::STATUS_AVAILABLE) {
+ return;
+ }
+
+// This test is disabled on arm due to a kernel bug.
+// See https://code.google.com/p/chromium/issues/detail?id=383977
+#if defined(__arm__) || defined(__aarch64__)
+ printf("This test is currently disabled on ARM32/64 due to a kernel bug.");
+ return;
+#endif
+
+#if defined(__mips__)
+ // TODO: Figure out how to support specificity of handling indirect syscalls
+ // in this test and enable it.
+ printf("This test is currently disabled on MIPS.");
+ return;
+#endif
+
+ pid_t pid = fork();
+ BPF_ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ pid_t my_pid = getpid();
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_TRACEME, -1, NULL, NULL));
+ BPF_ASSERT_EQ(0, raise(SIGSTOP));
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new TraceAllPolicy);
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ // getpid is allowed.
+ BPF_ASSERT_EQ(my_pid, syscall(__NR_getpid));
+
+ // write to stdout is skipped and returns a fake value.
+ BPF_ASSERT_EQ(kExpectedReturnValue,
+ syscall(__NR_write, STDOUT_FILENO, "A", 1));
+
+ // kill is rewritten to exit(kExpectedReturnValue).
+ syscall(__NR_kill, my_pid, SIGKILL);
+
+ // Should not be reached.
+ BPF_ASSERT(false);
+ }
+
+ int status;
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, WUNTRACED)) != -1);
+ BPF_ASSERT(WIFSTOPPED(status));
+
+ BPF_ASSERT_NE(-1,
+ ptrace(PTRACE_SETOPTIONS,
+ pid,
+ NULL,
+ reinterpret_cast<void*>(PTRACE_O_TRACESECCOMP)));
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ while (true) {
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, 0)) != -1);
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ BPF_ASSERT(WIFEXITED(status));
+ BPF_ASSERT_EQ(kExpectedReturnValue, WEXITSTATUS(status));
+ break;
+ }
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
+ !IS_SECCOMP_EVENT(status)) {
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ continue;
+ }
+
+ unsigned long data;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data));
+ BPF_ASSERT_EQ(kTraceData, data);
+
+ regs_struct regs;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_GETREGS, pid, NULL, ®s));
+ switch (SECCOMP_PT_SYSCALL(regs)) {
+ case __NR_write:
+ // Skip writes to stdout, make it return kExpectedReturnValue. Allow
+ // writes to stderr so that BPF_ASSERT messages show up.
+ if (SECCOMP_PT_PARM1(regs) == STDOUT_FILENO) {
+ BPF_ASSERT_NE(-1, SetSyscall(pid, ®s, -1));
+ SECCOMP_PT_RESULT(regs) = kExpectedReturnValue;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, ®s));
+ }
+ break;
+
+ case __NR_kill:
+ // Rewrite to exit(kExpectedReturnValue).
+ BPF_ASSERT_NE(-1, SetSyscall(pid, ®s, __NR_exit));
+ SECCOMP_PT_PARM1(regs) = kExpectedReturnValue;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, ®s));
+ break;
+
+ default:
+ // Allow all other syscalls.
+ break;
+ }
+
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ }
+}
+
+// Android does not expose pread64 nor pwrite64.
+#if !defined(OS_ANDROID)
+
+bool FullPwrite64(int fd, const char* buffer, size_t count, off64_t offset) {
+ while (count > 0) {
+ const ssize_t transfered =
+ HANDLE_EINTR(pwrite64(fd, buffer, count, offset));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ offset += transfered;
+ }
+ return true;
+}
+
+bool FullPread64(int fd, char* buffer, size_t count, off64_t offset) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(pread64(fd, buffer, count, offset));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ offset += transfered;
+ }
+ return true;
+}
+
+bool pread_64_was_forwarded = false;
+
+class TrapPread64Policy : public SandboxBPFDSLPolicy {
+ public:
+ TrapPread64Policy() {}
+ virtual ~TrapPread64Policy() {}
+
+ virtual ResultExpr EvaluateSyscall(int system_call_number) const override {
+ // Set the global environment for unsafe traps once.
+ if (system_call_number == MIN_SYSCALL) {
+ EnableUnsafeTraps();
+ }
+
+ if (system_call_number == __NR_pread64) {
+ return UnsafeTrap(ForwardPreadHandler, NULL);
+ }
+ return Allow();
+ }
+
+ private:
+ static intptr_t ForwardPreadHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ BPF_ASSERT(args.nr == __NR_pread64);
+ pread_64_was_forwarded = true;
+
+ return SandboxBPF::ForwardSyscall(args);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrapPread64Policy);
+};
+
+// pread(2) takes a 64 bits offset. On 32 bits systems, it will be split
+// between two arguments. In this test, we make sure that ForwardSyscall() can
+// forward it properly.
+BPF_TEST_C(SandboxBPF, Pread64, TrapPread64Policy) {
+ ScopedTemporaryFile temp_file;
+ const uint64_t kLargeOffset = (static_cast<uint64_t>(1) << 32) | 0xBEEF;
+ const char kTestString[] = "This is a test!";
+ BPF_ASSERT(FullPwrite64(
+ temp_file.fd(), kTestString, sizeof(kTestString), kLargeOffset));
+
+ char read_test_string[sizeof(kTestString)] = {0};
+ BPF_ASSERT(FullPread64(temp_file.fd(),
+ read_test_string,
+ sizeof(read_test_string),
+ kLargeOffset));
+ BPF_ASSERT_EQ(0, memcmp(kTestString, read_test_string, sizeof(kTestString)));
+ BPF_ASSERT(pread_64_was_forwarded);
+}
+
+#endif // !defined(OS_ANDROID)
+
+void* TsyncApplyToTwoThreadsFunc(void* cond_ptr) {
+ base::WaitableEvent* event = static_cast<base::WaitableEvent*>(cond_ptr);
+
+ // Wait for the main thread to signal that the filter has been applied.
+ if (!event->IsSignaled()) {
+ event->Wait();
+ }
+
+ BPF_ASSERT(event->IsSignaled());
+
+ BlacklistNanosleepPolicy::AssertNanosleepFails();
+
+ return NULL;
+}
+
+SANDBOX_TEST(SandboxBPF, Tsync) {
+ if (SandboxBPF::SupportsSeccompThreadFilterSynchronization() !=
+ SandboxBPF::STATUS_AVAILABLE) {
+ return;
+ }
+
+ base::WaitableEvent event(true, false);
+
+ // Create a thread on which to invoke the blocked syscall.
+ pthread_t thread;
+ BPF_ASSERT_EQ(
+ 0, pthread_create(&thread, NULL, &TsyncApplyToTwoThreadsFunc, &event));
+
+ // Test that nanoseelp success.
+ const struct timespec ts = {0, 0};
+ BPF_ASSERT_EQ(0, HANDLE_EINTR(syscall(__NR_nanosleep, &ts, NULL)));
+
+ // Engage the sandbox.
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new BlacklistNanosleepPolicy());
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_MULTI_THREADED));
+
+ // This thread should have the filter applied as well.
+ BlacklistNanosleepPolicy::AssertNanosleepFails();
+
+ // Signal the condition to invoke the system call.
+ event.Signal();
+
+ // Wait for the thread to finish.
+ BPF_ASSERT_EQ(0, pthread_join(thread, NULL));
+}
+
+class AllowAllPolicy : public SandboxBPFDSLPolicy {
+ public:
+ AllowAllPolicy() {}
+ virtual ~AllowAllPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
+};
+
+SANDBOX_DEATH_TEST(
+ SandboxBPF,
+ StartMultiThreadedAsSingleThreaded,
+ DEATH_MESSAGE("Cannot start sandbox; process is already multi-threaded")) {
+ base::Thread thread("sandbox.linux.StartMultiThreadedAsSingleThreaded");
+ BPF_ASSERT(thread.Start());
+
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new AllowAllPolicy());
+ BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+}
+
+// http://crbug.com/407357
+#if !defined(THREAD_SANITIZER)
+SANDBOX_DEATH_TEST(
+ SandboxBPF,
+ StartSingleThreadedAsMultiThreaded,
+ DEATH_MESSAGE(
+ "Cannot start sandbox; process may be single-threaded when "
+ "reported as not")) {
+ SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(new AllowAllPolicy());
+ BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::PROCESS_MULTI_THREADED));
+}
+#endif // !defined(THREAD_SANITIZER)
+
+// A stub handler for the UnsafeTrap. Never called.
+intptr_t NoOpHandler(const struct arch_seccomp_data& args, void*) {
+ return -1;
+}
+
+class UnsafeTrapWithCondPolicy : public SandboxBPFDSLPolicy {
+ public:
+ UnsafeTrapWithCondPolicy() {}
+ virtual ~UnsafeTrapWithCondPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ if (SandboxBPF::IsRequiredForUnsafeTrap(sysno))
+ return Allow();
+
+ switch (sysno) {
+ case __NR_uname: {
+ const Arg<uint32_t> arg(0);
+ return If(arg == 0, Allow()).Else(Error(EPERM));
+ }
+ case __NR_setgid: {
+ const Arg<uint32_t> arg(0);
+ return Switch(arg)
+ .Case(100, Error(ENOMEM))
+ .Case(200, Error(ENOSYS))
+ .Default(Error(EPERM));
+ }
+ case __NR_close:
+ case __NR_exit_group:
+ case __NR_write:
+ return Allow();
+ case __NR_getppid:
+ return UnsafeTrap(NoOpHandler, NULL);
+ default:
+ return Error(EPERM);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UnsafeTrapWithCondPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, UnsafeTrapWithCond, UnsafeTrapWithCondPolicy) {
+ BPF_ASSERT_EQ(-1, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(EFAULT, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_uname, 1));
+ BPF_ASSERT_EQ(EPERM, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_setgid, 100));
+ BPF_ASSERT_EQ(ENOMEM, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_setgid, 200));
+ BPF_ASSERT_EQ(ENOSYS, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_setgid, 300));
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+} // namespace
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
new file mode 100644
index 0000000..778bba8
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
@@ -0,0 +1,332 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#define CASES SANDBOX_BPF_DSL_CASES
+
+// Helper macro to assert that invoking system call |sys| directly via
+// Syscall::Call with arguments |...| returns |res|.
+// Errors can be asserted by specifying a value like "-EINVAL".
+#define ASSERT_SYSCALL_RESULT(res, sys, ...) \
+ BPF_ASSERT_EQ(res, Stubs::sys(__VA_ARGS__))
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+// Type safe stubs for tested system calls.
+class Stubs {
+ public:
+ static int getpgid(pid_t pid) { return Syscall::Call(__NR_getpgid, pid); }
+ static int setuid(uid_t uid) { return Syscall::Call(__NR_setuid, uid); }
+ static int setgid(gid_t gid) { return Syscall::Call(__NR_setgid, gid); }
+ static int setpgid(pid_t pid, pid_t pgid) {
+ return Syscall::Call(__NR_setpgid, pid, pgid);
+ }
+
+ static int fcntl(int fd, int cmd, unsigned long arg = 0) {
+ return Syscall::Call(__NR_fcntl, fd, cmd, arg);
+ }
+
+ static int uname(struct utsname* buf) {
+ return Syscall::Call(__NR_uname, buf);
+ }
+
+ static int setresuid(uid_t ruid, uid_t euid, uid_t suid) {
+ return Syscall::Call(__NR_setresuid, ruid, euid, suid);
+ }
+
+#if !defined(ARCH_CPU_X86)
+ static int socketpair(int domain, int type, int protocol, int sv[2]) {
+ return Syscall::Call(__NR_socketpair, domain, type, protocol, sv);
+ }
+#endif
+};
+
+class BasicPolicy : public SandboxBPFDSLPolicy {
+ public:
+ BasicPolicy() {}
+ virtual ~BasicPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_getpgid) {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0, Error(EPERM)).Else(Error(EINVAL));
+ }
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If(uid != 42, Error(ESRCH)).Else(Error(ENOMEM));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicPolicy);
+};
+
+BPF_TEST_C(BPFDSL, Basic, BasicPolicy) {
+ ASSERT_SYSCALL_RESULT(-EPERM, getpgid, 0);
+ ASSERT_SYSCALL_RESULT(-EINVAL, getpgid, 1);
+
+ ASSERT_SYSCALL_RESULT(-ENOMEM, setuid, 42);
+ ASSERT_SYSCALL_RESULT(-ESRCH, setuid, 43);
+}
+
+/* On IA-32, socketpair() is implemented via socketcall(). :-( */
+#if !defined(ARCH_CPU_X86)
+class BooleanLogicPolicy : public SandboxBPFDSLPolicy {
+ public:
+ BooleanLogicPolicy() {}
+ virtual ~BooleanLogicPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_socketpair) {
+ const Arg<int> domain(0), type(1), protocol(2);
+ return If(domain == AF_UNIX &&
+ (type == SOCK_STREAM || type == SOCK_DGRAM) &&
+ protocol == 0,
+ Error(EPERM)).Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy);
+};
+
+BPF_TEST_C(BPFDSL, BooleanLogic, BooleanLogicPolicy) {
+ int sv[2];
+
+ // Acceptable combinations that should return EPERM.
+ ASSERT_SYSCALL_RESULT(-EPERM, socketpair, AF_UNIX, SOCK_STREAM, 0, sv);
+ ASSERT_SYSCALL_RESULT(-EPERM, socketpair, AF_UNIX, SOCK_DGRAM, 0, sv);
+
+ // Combinations that are invalid for only one reason; should return EINVAL.
+ ASSERT_SYSCALL_RESULT(-EINVAL, socketpair, AF_INET, SOCK_STREAM, 0, sv);
+ ASSERT_SYSCALL_RESULT(-EINVAL, socketpair, AF_UNIX, SOCK_SEQPACKET, 0, sv);
+ ASSERT_SYSCALL_RESULT(
+ -EINVAL, socketpair, AF_UNIX, SOCK_STREAM, IPPROTO_TCP, sv);
+
+ // Completely unacceptable combination; should also return EINVAL.
+ ASSERT_SYSCALL_RESULT(
+ -EINVAL, socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP, sv);
+}
+#endif // !ARCH_CPU_X86
+
+class MoreBooleanLogicPolicy : public SandboxBPFDSLPolicy {
+ public:
+ MoreBooleanLogicPolicy() {}
+ virtual ~MoreBooleanLogicPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setresuid) {
+ const Arg<uid_t> ruid(0), euid(1), suid(2);
+ return If(ruid == 0 || euid == 0 || suid == 0, Error(EPERM))
+ .ElseIf(ruid == 1 && euid == 1 && suid == 1, Error(EAGAIN))
+ .Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy);
+};
+
+BPF_TEST_C(BPFDSL, MoreBooleanLogic, MoreBooleanLogicPolicy) {
+ // Expect EPERM if any set to 0.
+ ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 0, 5, 5);
+ ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 5, 0, 5);
+ ASSERT_SYSCALL_RESULT(-EPERM, setresuid, 5, 5, 0);
+
+ // Expect EAGAIN if all set to 1.
+ ASSERT_SYSCALL_RESULT(-EAGAIN, setresuid, 1, 1, 1);
+
+ // Expect EINVAL for anything else.
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 5, 1, 1);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 1, 5, 1);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 1, 1, 5);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setresuid, 3, 4, 5);
+}
+
+static const uintptr_t kDeadBeefAddr =
+ static_cast<uintptr_t>(0xdeadbeefdeadbeefULL);
+
+class ArgSizePolicy : public SandboxBPFDSLPolicy {
+ public:
+ ArgSizePolicy() {}
+ virtual ~ArgSizePolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_uname) {
+ const Arg<uintptr_t> addr(0);
+ return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow());
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy);
+};
+
+BPF_TEST_C(BPFDSL, ArgSizeTest, ArgSizePolicy) {
+ struct utsname buf;
+ ASSERT_SYSCALL_RESULT(0, uname, &buf);
+ ASSERT_SYSCALL_RESULT(
+ -EPERM, uname, reinterpret_cast<struct utsname*>(kDeadBeefAddr));
+}
+
+class TrappingPolicy : public SandboxBPFDSLPolicy {
+ public:
+ TrappingPolicy() {}
+ virtual ~TrappingPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_uname) {
+ return Trap(UnameTrap, &count_);
+ }
+ return Allow();
+ }
+
+ private:
+ static intptr_t count_;
+
+ static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) {
+ BPF_ASSERT_EQ(&count_, aux);
+ return ++count_;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrappingPolicy);
+};
+
+intptr_t TrappingPolicy::count_;
+
+BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) {
+ ASSERT_SYSCALL_RESULT(1, uname, NULL);
+ ASSERT_SYSCALL_RESULT(2, uname, NULL);
+ ASSERT_SYSCALL_RESULT(3, uname, NULL);
+}
+
+class MaskingPolicy : public SandboxBPFDSLPolicy {
+ public:
+ MaskingPolicy() {}
+ virtual ~MaskingPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setgid) {
+ const Arg<gid_t> gid(0);
+ return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setpgid) {
+ const Arg<pid_t> pid(0);
+ return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MaskingPolicy);
+};
+
+BPF_TEST_C(BPFDSL, MaskTest, MaskingPolicy) {
+ for (uid_t uid = 0; uid < 0x100; ++uid) {
+ const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES;
+ ASSERT_SYSCALL_RESULT(-expect_errno, setuid, uid);
+ }
+
+ for (gid_t gid = 0; gid < 0x100; ++gid) {
+ const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES;
+ ASSERT_SYSCALL_RESULT(-expect_errno, setgid, gid);
+ }
+
+ for (pid_t pid = 0; pid < 0x100; ++pid) {
+ const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES;
+ ASSERT_SYSCALL_RESULT(-expect_errno, setpgid, pid, 0);
+ }
+}
+
+class ElseIfPolicy : public SandboxBPFDSLPolicy {
+ public:
+ ElseIfPolicy() {}
+ virtual ~ElseIfPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xfff) == 0, Error(0))
+ .ElseIf((uid & 0xff0) == 0, Error(EINVAL))
+ .ElseIf((uid & 0xf00) == 0, Error(EEXIST))
+ .Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy);
+};
+
+BPF_TEST_C(BPFDSL, ElseIfTest, ElseIfPolicy) {
+ ASSERT_SYSCALL_RESULT(0, setuid, 0);
+
+ ASSERT_SYSCALL_RESULT(-EINVAL, setuid, 0x0001);
+ ASSERT_SYSCALL_RESULT(-EINVAL, setuid, 0x0002);
+
+ ASSERT_SYSCALL_RESULT(-EEXIST, setuid, 0x0011);
+ ASSERT_SYSCALL_RESULT(-EEXIST, setuid, 0x0022);
+
+ ASSERT_SYSCALL_RESULT(-EACCES, setuid, 0x0111);
+ ASSERT_SYSCALL_RESULT(-EACCES, setuid, 0x0222);
+}
+
+class SwitchPolicy : public SandboxBPFDSLPolicy {
+ public:
+ SwitchPolicy() {}
+ virtual ~SwitchPolicy() {}
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_fcntl) {
+ const Arg<int> cmd(1);
+ const Arg<unsigned long> long_arg(2);
+ return Switch(cmd)
+ .CASES((F_GETFL, F_GETFD), Error(ENOENT))
+ .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL)))
+ .Case(F_SETFL, Error(EPERM))
+ .Default(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SwitchPolicy);
+};
+
+BPF_TEST_C(BPFDSL, SwitchTest, SwitchPolicy) {
+ base::ScopedFD sock_fd(socket(AF_UNIX, SOCK_STREAM, 0));
+ BPF_ASSERT(sock_fd.is_valid());
+
+ ASSERT_SYSCALL_RESULT(-ENOENT, fcntl, sock_fd.get(), F_GETFD);
+ ASSERT_SYSCALL_RESULT(-ENOENT, fcntl, sock_fd.get(), F_GETFL);
+
+ ASSERT_SYSCALL_RESULT(0, fcntl, sock_fd.get(), F_SETFD, O_CLOEXEC);
+ ASSERT_SYSCALL_RESULT(-EINVAL, fcntl, sock_fd.get(), F_SETFD, 0);
+
+ ASSERT_SYSCALL_RESULT(-EPERM, fcntl, sock_fd.get(), F_SETFL, O_RDONLY);
+
+ ASSERT_SYSCALL_RESULT(-EACCES, fcntl, sock_fd.get(), F_DUPFD, 0);
+}
+
+} // namespace
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/cons.h b/sandbox/linux/bpf_dsl/cons.h
new file mode 100644
index 0000000..fa47c14
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/cons.h
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_CONS_H_
+#define SANDBOX_LINUX_BPF_DSL_CONS_H_
+
+#include "base/memory/ref_counted.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace cons {
+
+// Namespace cons provides an abstraction for immutable "cons list"
+// data structures as commonly provided in functional programming
+// languages like Lisp or Haskell.
+//
+// A cons list is a linked list consisting of "cells", each of which
+// have a "head" and a "tail" element. A cell's head element contains
+// a user specified value, while the tail element contains a (possibly
+// null) pointer to another cell.
+//
+// An empty list (idiomatically referred to as "nil") can be
+// constructed as "cons::List<Foo>()" or simply as "nullptr" if Foo
+// can be inferred from context (e.g., calling a function that has a
+// "cons::List<Foo>" parameter).
+//
+// Existing lists (including empty lists) can be extended by
+// prepending new values to the front using the "Cons(head, tail)"
+// function, which will allocate a new cons cell. Notably, cons lists
+// support creating multiple lists that share a common tail sequence.
+//
+// Lastly, lists support iteration via C++11's range-based for loop
+// construct.
+//
+// Examples:
+//
+// // basic construction
+// const cons::List<char> kNil = nullptr;
+// cons::List<char> ba = Cons('b', Cons('a', kNil));
+//
+// // common tail sequence
+// cons::List<char> cba = Cons('c', ba);
+// cons::List<char> dba = Cons('d', ba);
+//
+// // iteration
+// for (const char& ch : cba) {
+// // iterates 'c', 'b', 'a'
+// }
+// for (const char& ch : dba) {
+// // iterates 'd', 'b', 'a'
+// }
+
+// Forward declarations.
+template <typename T>
+class Cell;
+template <typename T>
+class ListIterator;
+
+// List represents a (possibly null) pointer to a cons cell.
+template <typename T>
+using List = scoped_refptr<const Cell<T>>;
+
+// Cons extends a cons list by prepending a new value to the front.
+template <typename T>
+List<T> Cons(const T& head, const List<T>& tail) {
+ return List<T>(new const Cell<T>(head, tail));
+}
+
+// Cell represents an individual "cons cell" within a cons list.
+template <typename T>
+class Cell : public base::RefCounted<Cell<T>> {
+ public:
+ Cell(const T& head, const List<T>& tail) : head_(head), tail_(tail) {}
+
+ // Head returns this cell's head element.
+ const T& head() const { return head_; }
+
+ // Tail returns this cell's tail element.
+ const List<T>& tail() const { return tail_; }
+
+ private:
+ virtual ~Cell() {}
+
+ T head_;
+ List<T> tail_;
+
+ friend class base::RefCounted<Cell<T>>;
+ DISALLOW_COPY_AND_ASSIGN(Cell);
+};
+
+// Begin returns a list iterator pointing to the first element of the
+// cons list. It's provided to support range-based for loops.
+template <typename T>
+ListIterator<T> begin(const List<T>& list) {
+ return ListIterator<T>(list);
+}
+
+// End returns a list iterator pointing to the "past-the-end" element
+// of the cons list (i.e., nil). It's provided to support range-based
+// for loops.
+template <typename T>
+ListIterator<T> end(const List<T>& list) {
+ return ListIterator<T>();
+}
+
+// ListIterator provides C++ forward iterator semantics for traversing
+// a cons list.
+template <typename T>
+class ListIterator {
+ public:
+ ListIterator() : list_() {}
+ explicit ListIterator(const List<T>& list) : list_(list) {}
+
+ const T& operator*() const { return list_->head(); }
+
+ ListIterator& operator++() {
+ list_ = list_->tail();
+ return *this;
+ }
+
+ friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) {
+ return lhs.list_ == rhs.list_;
+ }
+
+ private:
+ List<T> list_;
+};
+
+template <typename T>
+bool operator!=(const ListIterator<T>& lhs, const ListIterator<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace cons
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_CONS_H_
diff --git a/sandbox/linux/bpf_dsl/cons_unittest.cc b/sandbox/linux/bpf_dsl/cons_unittest.cc
new file mode 100644
index 0000000..ea2ba2f
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/cons_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/cons.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+std::string Join(cons::List<char> char_list) {
+ std::string res;
+ for (const char& ch : char_list) {
+ res.push_back(ch);
+ }
+ return res;
+}
+
+TEST(ConsListTest, Basic) {
+ cons::List<char> ba = Cons('b', Cons('a', cons::List<char>()));
+ EXPECT_EQ("ba", Join(ba));
+
+ cons::List<char> cba = Cons('c', ba);
+ cons::List<char> dba = Cons('d', ba);
+ EXPECT_EQ("cba", Join(cba));
+ EXPECT_EQ("dba", Join(dba));
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy_compiler.cc b/sandbox/linux/bpf_dsl/policy_compiler.cc
new file mode 100644
index 0000000..0eb85ca
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy_compiler.cc
@@ -0,0 +1,523 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+
+#include <errno.h>
+#include <linux/filter.h>
+#include <sys/syscall.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/instruction.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+#if defined(__i386__) || defined(__x86_64__)
+const bool kIsIntel = true;
+#else
+const bool kIsIntel = false;
+#endif
+#if defined(__x86_64__) && defined(__ILP32__)
+const bool kIsX32 = true;
+#else
+const bool kIsX32 = false;
+#endif
+
+const int kSyscallsRequiredForUnsafeTraps[] = {
+ __NR_rt_sigprocmask,
+ __NR_rt_sigreturn,
+#if defined(__NR_sigprocmask)
+ __NR_sigprocmask,
+#endif
+#if defined(__NR_sigreturn)
+ __NR_sigreturn,
+#endif
+};
+
+bool HasExactlyOneBit(uint64_t x) {
+ // Common trick; e.g., see http://stackoverflow.com/a/108329.
+ return x != 0 && (x & (x - 1)) == 0;
+}
+
+bool IsDenied(const ErrorCode& code) {
+ return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
+ (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
+ code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
+}
+
+// A Trap() handler that returns an "errno" value. The value is encoded
+// in the "aux" parameter.
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) {
+ // TrapFnc functions report error by following the native kernel convention
+ // of returning an exit code in the range of -1..-4096. They do not try to
+ // set errno themselves. The glibc wrapper that triggered the SIGSYS will
+ // ultimately do so for us.
+ int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
+ return -err;
+}
+
+intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
+ SANDBOX_DIE(static_cast<char*>(aux));
+}
+
+bool HasUnsafeTraps(const SandboxBPFDSLPolicy* policy) {
+ for (uint32_t sysnum : SyscallSet::All()) {
+ if (SyscallSet::IsValid(sysnum) &&
+ policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
+ return true;
+ }
+ }
+ return policy->InvalidSyscall()->HasUnsafeTraps();
+}
+
+} // namespace
+
+struct PolicyCompiler::Range {
+ Range(uint32_t f, const ErrorCode& e) : from(f), err(e) {}
+ uint32_t from;
+ ErrorCode err;
+};
+
+PolicyCompiler::PolicyCompiler(const SandboxBPFDSLPolicy* policy,
+ TrapRegistry* registry)
+ : policy_(policy),
+ registry_(registry),
+ conds_(),
+ gen_(),
+ has_unsafe_traps_(HasUnsafeTraps(policy_)) {
+}
+
+PolicyCompiler::~PolicyCompiler() {
+}
+
+scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() {
+ if (!IsDenied(policy_->InvalidSyscall()->Compile(this))) {
+ SANDBOX_DIE("Policies should deny invalid system calls.");
+ }
+
+ // If our BPF program has unsafe traps, enable support for them.
+ if (has_unsafe_traps_) {
+ // As support for unsafe jumps essentially defeats all the security
+ // measures that the sandbox provides, we print a big warning message --
+ // and of course, we make sure to only ever enable this feature if it
+ // is actually requested by the sandbox policy.
+ if (Syscall::Call(-1) == -1 && errno == ENOSYS) {
+ SANDBOX_DIE(
+ "Support for UnsafeTrap() has not yet been ported to this "
+ "architecture");
+ }
+
+ for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
+ if (!policy_->EvaluateSyscall(sysnum)->Compile(this)
+ .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))) {
+ SANDBOX_DIE(
+ "Policies that use UnsafeTrap() must unconditionally allow all "
+ "required system calls");
+ }
+ }
+
+ if (!registry_->EnableUnsafeTraps()) {
+ // We should never be able to get here, as UnsafeTrap() should never
+ // actually return a valid ErrorCode object unless the user set the
+ // CHROME_SANDBOX_DEBUGGING environment variable; and therefore,
+ // "has_unsafe_traps" would always be false. But better double-check
+ // than enabling dangerous code.
+ SANDBOX_DIE("We'd rather die than enable unsafe traps");
+ }
+ }
+
+ // Assemble the BPF filter program.
+ scoped_ptr<CodeGen::Program> program(new CodeGen::Program());
+ gen_.Compile(AssemblePolicy(), program.get());
+ return program.Pass();
+}
+
+Instruction* PolicyCompiler::AssemblePolicy() {
+ // A compiled policy consists of three logical parts:
+ // 1. Check that the "arch" field matches the expected architecture.
+ // 2. If the policy involves unsafe traps, check if the syscall was
+ // invoked by Syscall::Call, and then allow it unconditionally.
+ // 3. Check the system call number and jump to the appropriate compiled
+ // system call policy number.
+ return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
+}
+
+Instruction* PolicyCompiler::CheckArch(Instruction* passed) {
+ // If the architecture doesn't match SECCOMP_ARCH, disallow the
+ // system call.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_ARCH_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ SECCOMP_ARCH,
+ passed,
+ RetExpression(Kill("Invalid audit architecture in BPF filter"))));
+}
+
+Instruction* PolicyCompiler::MaybeAddEscapeHatch(Instruction* rest) {
+ // If no unsafe traps, then simply return |rest|.
+ if (!has_unsafe_traps_) {
+ return rest;
+ }
+
+ // Allow system calls, if they originate from our magic return address
+ // (which we can query by calling Syscall::Call(-1)).
+ uint64_t syscall_entry_point =
+ static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1)));
+ uint32_t low = static_cast<uint32_t>(syscall_entry_point);
+ uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32);
+
+ // BPF cannot do native 64-bit comparisons, so we have to compare
+ // both 32-bit halves of the instruction pointer. If they match what
+ // we expect, we return ERR_ALLOWED. If either or both don't match,
+ // we continue evalutating the rest of the sandbox policy.
+ //
+ // For simplicity, we check the full 64-bit instruction pointer even
+ // on 32-bit architectures.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_IP_LSB_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ low,
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ SECCOMP_IP_MSB_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ hi,
+ RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)),
+ rest)),
+ rest));
+}
+
+Instruction* PolicyCompiler::DispatchSyscall() {
+ // Evaluate all possible system calls and group their ErrorCodes into
+ // ranges of identical codes.
+ Ranges ranges;
+ FindRanges(&ranges);
+
+ // Compile the system call ranges to an optimized BPF jumptable
+ Instruction* jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
+
+ // Grab the system call number, so that we can check it and then
+ // execute the jump table.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable));
+}
+
+Instruction* PolicyCompiler::CheckSyscallNumber(Instruction* passed) {
+ if (kIsIntel) {
+ // On Intel architectures, verify that system call numbers are in the
+ // expected number range.
+ Instruction* invalidX32 =
+ RetExpression(Kill("Illegal mixing of system call ABIs"));
+ if (kIsX32) {
+ // The newer x32 API always sets bit 30.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32);
+ } else {
+ // The older i386 and x86-64 APIs clear bit 30 on all system calls.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed);
+ }
+ }
+
+ // TODO(mdempsky): Similar validation for other architectures?
+ return passed;
+}
+
+void PolicyCompiler::FindRanges(Ranges* ranges) {
+ // Please note that "struct seccomp_data" defines system calls as a signed
+ // int32_t, but BPF instructions always operate on unsigned quantities. We
+ // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
+ // and then verifying that the rest of the number range (both positive and
+ // negative) all return the same ErrorCode.
+ const ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this);
+ uint32_t old_sysnum = 0;
+ ErrorCode old_err = SyscallSet::IsValid(old_sysnum)
+ ? policy_->EvaluateSyscall(old_sysnum)->Compile(this)
+ : invalid_err;
+
+ for (uint32_t sysnum : SyscallSet::All()) {
+ ErrorCode err =
+ SyscallSet::IsValid(sysnum)
+ ? policy_->EvaluateSyscall(static_cast<int>(sysnum))->Compile(this)
+ : invalid_err;
+ if (!err.Equals(old_err)) {
+ ranges->push_back(Range(old_sysnum, old_err));
+ old_sysnum = sysnum;
+ old_err = err;
+ }
+ }
+ ranges->push_back(Range(old_sysnum, old_err));
+}
+
+Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop) {
+ // We convert the list of system call ranges into jump table that performs
+ // a binary search over the ranges.
+ // As a sanity check, we need to have at least one distinct ranges for us
+ // to be able to build a jump table.
+ if (stop - start <= 0) {
+ SANDBOX_DIE("Invalid set of system call ranges");
+ } else if (stop - start == 1) {
+ // If we have narrowed things down to a single range object, we can
+ // return from the BPF filter program.
+ return RetExpression(start->err);
+ }
+
+ // Pick the range object that is located at the mid point of our list.
+ // We compare our system call number against the lowest valid system call
+ // number in this range object. If our number is lower, it is outside of
+ // this range object. If it is greater or equal, it might be inside.
+ Ranges::const_iterator mid = start + (stop - start) / 2;
+
+ // Sub-divide the list of ranges and continue recursively.
+ Instruction* jf = AssembleJumpTable(start, mid);
+ Instruction* jt = AssembleJumpTable(mid, stop);
+ return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
+}
+
+Instruction* PolicyCompiler::RetExpression(const ErrorCode& err) {
+ switch (err.error_type()) {
+ case ErrorCode::ET_COND:
+ return CondExpression(err);
+ case ErrorCode::ET_SIMPLE:
+ case ErrorCode::ET_TRAP:
+ return gen_.MakeInstruction(BPF_RET + BPF_K, err.err());
+ default:
+ SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program");
+ }
+}
+
+Instruction* PolicyCompiler::CondExpression(const ErrorCode& cond) {
+ // Sanity check that |cond| makes sense.
+ if (cond.argno_ < 0 || cond.argno_ >= 6) {
+ SANDBOX_DIE("sandbox_bpf: invalid argument number");
+ }
+ if (cond.width_ != ErrorCode::TP_32BIT &&
+ cond.width_ != ErrorCode::TP_64BIT) {
+ SANDBOX_DIE("sandbox_bpf: invalid argument width");
+ }
+ if (cond.mask_ == 0) {
+ SANDBOX_DIE("sandbox_bpf: zero mask is invalid");
+ }
+ if ((cond.value_ & cond.mask_) != cond.value_) {
+ SANDBOX_DIE("sandbox_bpf: value contains masked out bits");
+ }
+ if (cond.width_ == ErrorCode::TP_32BIT &&
+ ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) {
+ SANDBOX_DIE("sandbox_bpf: test exceeds argument size");
+ }
+ // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it
+ // because some SandboxBPF unit tests exercise it.
+
+ Instruction* passed = RetExpression(*cond.passed_);
+ Instruction* failed = RetExpression(*cond.failed_);
+
+ // We want to emit code to check "(arg & mask) == value" where arg, mask, and
+ // value are 64-bit values, but the BPF machine is only 32-bit. We implement
+ // this by independently testing the upper and lower 32-bits and continuing to
+ // |passed| if both evaluate true, or to |failed| if either evaluate false.
+ return CondExpressionHalf(cond,
+ UpperHalf,
+ CondExpressionHalf(cond, LowerHalf, passed, failed),
+ failed);
+}
+
+Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond,
+ ArgHalf half,
+ Instruction* passed,
+ Instruction* failed) {
+ if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) {
+ // Special logic for sanity checking the upper 32-bits of 32-bit system
+ // call arguments.
+
+ // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
+ Instruction* invalid_64bit = RetExpression(Unexpected64bitArgument());
+
+ const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_);
+ const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_);
+
+ if (sizeof(void*) == 4) {
+ // On 32-bit platforms, the upper 32-bits should always be 0:
+ // LDW [upper]
+ // JEQ 0, passed, invalid
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
+ }
+
+ // On 64-bit platforms, the upper 32-bits may be 0 or ~0; but we only allow
+ // ~0 if the sign bit of the lower 32-bits is set too:
+ // LDW [upper]
+ // JEQ 0, passed, (next)
+ // JEQ ~0, (next), invalid
+ // LDW [lower]
+ // JSET (1<<31), passed, invalid
+ //
+ // TODO(mdempsky): The JSET instruction could perhaps jump to passed->next
+ // instead, as the first instruction of passed should be "LDW [lower]".
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 0,
+ passed,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ std::numeric_limits<uint32_t>::max(),
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ lower,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ 1U << 31,
+ passed,
+ invalid_64bit)),
+ invalid_64bit)));
+ }
+
+ const uint32_t idx = (half == UpperHalf) ? SECCOMP_ARG_MSB_IDX(cond.argno_)
+ : SECCOMP_ARG_LSB_IDX(cond.argno_);
+ const uint32_t mask = (half == UpperHalf) ? cond.mask_ >> 32 : cond.mask_;
+ const uint32_t value = (half == UpperHalf) ? cond.value_ >> 32 : cond.value_;
+
+ // Emit a suitable instruction sequence for (arg & mask) == value.
+
+ // For (arg & 0) == 0, just return passed.
+ if (mask == 0) {
+ CHECK_EQ(0U, value);
+ return passed;
+ }
+
+ // For (arg & ~0) == value, emit:
+ // LDW [idx]
+ // JEQ value, passed, failed
+ if (mask == std::numeric_limits<uint32_t>::max()) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed));
+ }
+
+ // For (arg & mask) == 0, emit:
+ // LDW [idx]
+ // JSET mask, failed, passed
+ // (Note: failed and passed are intentionally swapped.)
+ if (value == 0) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, failed, passed));
+ }
+
+ // For (arg & x) == x where x is a single-bit value, emit:
+ // LDW [idx]
+ // JSET mask, passed, failed
+ if (mask == value && HasExactlyOneBit(mask)) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, passed, failed));
+ }
+
+ // Generic fallback:
+ // LDW [idx]
+ // AND mask
+ // JEQ value, passed, failed
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(
+ BPF_ALU + BPF_AND + BPF_K,
+ mask,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
+}
+
+ErrorCode PolicyCompiler::Unexpected64bitArgument() {
+ return Kill("Unexpected 64bit argument detected");
+}
+
+ErrorCode PolicyCompiler::Error(int err) {
+ if (has_unsafe_traps_) {
+ // When inside an UnsafeTrap() callback, we want to allow all system calls.
+ // This means, we must conditionally disable the sandbox -- and that's not
+ // something that kernel-side BPF filters can do, as they cannot inspect
+ // any state other than the syscall arguments.
+ // But if we redirect all error handlers to user-space, then we can easily
+ // make this decision.
+ // The performance penalty for this extra round-trip to user-space is not
+ // actually that bad, as we only ever pay it for denied system calls; and a
+ // typical program has very few of these.
+ return Trap(ReturnErrno, reinterpret_cast<void*>(err));
+ }
+
+ return ErrorCode(err);
+}
+
+ErrorCode PolicyCompiler::MakeTrap(TrapRegistry::TrapFnc fnc,
+ const void* aux,
+ bool safe) {
+ uint16_t trap_id = registry_->Add(fnc, aux, safe);
+ return ErrorCode(trap_id, fnc, aux, safe);
+}
+
+ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, const void* aux) {
+ return MakeTrap(fnc, aux, true /* Safe Trap */);
+}
+
+ErrorCode PolicyCompiler::UnsafeTrap(TrapRegistry::TrapFnc fnc,
+ const void* aux) {
+ return MakeTrap(fnc, aux, false /* Unsafe Trap */);
+}
+
+bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) {
+ for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) {
+ if (sysno == kSyscallsRequiredForUnsafeTraps[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ErrorCode PolicyCompiler::CondMaskedEqual(int argno,
+ ErrorCode::ArgType width,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed) {
+ return ErrorCode(argno,
+ width,
+ mask,
+ value,
+ &*conds_.insert(passed).first,
+ &*conds_.insert(failed).first);
+}
+
+ErrorCode PolicyCompiler::Kill(const char* msg) {
+ return Trap(BPFFailure, const_cast<char*>(msg));
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy_compiler.h b/sandbox/linux/bpf_dsl/policy_compiler.h
new file mode 100644
index 0000000..84712ef
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy_compiler.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+struct Instruction;
+
+namespace bpf_dsl {
+class SandboxBPFDSLPolicy;
+
+// PolicyCompiler implements the bpf_dsl compiler, allowing users to
+// transform bpf_dsl policies into BPF programs to be executed by the
+// Linux kernel.
+class SANDBOX_EXPORT PolicyCompiler {
+ public:
+ PolicyCompiler(const SandboxBPFDSLPolicy* policy, TrapRegistry* registry);
+ ~PolicyCompiler();
+
+ // Compile registers any trap handlers needed by the policy and
+ // compiles the policy to a BPF program, which it returns.
+ scoped_ptr<CodeGen::Program> Compile();
+
+ // Error returns an ErrorCode to indicate the system call should fail with
+ // the specified error number.
+ ErrorCode Error(int err);
+
+ // We can use ErrorCode to request calling of a trap handler. This method
+ // performs the required wrapping of the callback function into an
+ // ErrorCode object.
+ // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
+ // for a description of how to pass data from SetSandboxPolicy() to a Trap()
+ // handler.
+ ErrorCode Trap(TrapRegistry::TrapFnc fnc, const void* aux);
+
+ // Calls a user-space trap handler and disables all sandboxing for system
+ // calls made from this trap handler.
+ // This feature is available only if explicitly enabled by the user having
+ // set the CHROME_SANDBOX_DEBUGGING environment variable.
+ // Returns an ET_INVALID ErrorCode, if called when not enabled.
+ // NOTE: This feature, by definition, disables all security features of
+ // the sandbox. It should never be used in production, but it can be
+ // very useful to diagnose code that is incompatible with the sandbox.
+ // If even a single system call returns "UnsafeTrap", the security of
+ // entire sandbox should be considered compromised.
+ ErrorCode UnsafeTrap(TrapRegistry::TrapFnc fnc, const void* aux);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // We can also use ErrorCode to request evaluation of a conditional
+ // statement based on inspection of system call parameters.
+ // This method wrap an ErrorCode object around the conditional statement.
+ // Argument "argno" (1..6) will be bitwise-AND'd with "mask" and compared
+ // to "value"; if equal, then "passed" will be returned, otherwise "failed".
+ // If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1)
+ // If it is outside this range, the sandbox treats the system call just
+ // the same as any other ABI violation (i.e. it aborts with an error
+ // message).
+ ErrorCode CondMaskedEqual(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed);
+
+ // Kill the program and print an error message.
+ ErrorCode Kill(const char* msg);
+
+ // Returns the fatal ErrorCode that is used to indicate that somebody
+ // attempted to pass a 64bit value in a 32bit system call argument.
+ // This method is primarily needed for testing purposes.
+ ErrorCode Unexpected64bitArgument();
+
+ private:
+ struct Range;
+ typedef std::vector<Range> Ranges;
+ typedef std::map<uint32_t, ErrorCode> ErrMap;
+ typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
+
+ // Used by CondExpressionHalf to track which half of the argument it's
+ // emitting instructions for.
+ enum ArgHalf {
+ LowerHalf,
+ UpperHalf,
+ };
+
+ // Compile the configured policy into a complete instruction sequence.
+ Instruction* AssemblePolicy();
+
+ // Return an instruction sequence that checks the
+ // arch_seccomp_data's "arch" field is valid, and then passes
+ // control to |passed| if so.
+ Instruction* CheckArch(Instruction* passed);
+
+ // If |has_unsafe_traps_| is true, returns an instruction sequence
+ // that allows all system calls from Syscall::Call(), and otherwise
+ // passes control to |rest|. Otherwise, simply returns |rest|.
+ Instruction* MaybeAddEscapeHatch(Instruction* rest);
+
+ // Return an instruction sequence that loads and checks the system
+ // call number, performs a binary search, and then dispatches to an
+ // appropriate instruction sequence compiled from the current
+ // policy.
+ Instruction* DispatchSyscall();
+
+ // Return an instruction sequence that checks the system call number
+ // (expected to be loaded in register A) and if valid, passes
+ // control to |passed| (with register A still valid).
+ Instruction* CheckSyscallNumber(Instruction* passed);
+
+ // Finds all the ranges of system calls that need to be handled. Ranges are
+ // sorted in ascending order of system call numbers. There are no gaps in the
+ // ranges. System calls with identical ErrorCodes are coalesced into a single
+ // range.
+ void FindRanges(Ranges* ranges);
+
+ // Returns a BPF program snippet that implements a jump table for the
+ // given range of system call numbers. This function runs recursively.
+ Instruction* AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop);
+
+ // Returns a BPF program snippet that makes the BPF filter program exit
+ // with the given ErrorCode "err". N.B. the ErrorCode may very well be a
+ // conditional expression; if so, this function will recursively call
+ // CondExpression() and possibly RetExpression() to build a complex set of
+ // instructions.
+ Instruction* RetExpression(const ErrorCode& err);
+
+ // Returns a BPF program that evaluates the conditional expression in
+ // "cond" and returns the appropriate value from the BPF filter program.
+ // This function recursively calls RetExpression(); it should only ever be
+ // called from RetExpression().
+ Instruction* CondExpression(const ErrorCode& cond);
+
+ // Returns a BPF program that evaluates half of a conditional expression;
+ // it should only ever be called from CondExpression().
+ Instruction* CondExpressionHalf(const ErrorCode& cond,
+ ArgHalf half,
+ Instruction* passed,
+ Instruction* failed);
+
+ // MakeTrap is the common implementation for Trap and UnsafeTrap.
+ ErrorCode MakeTrap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe);
+
+ const SandboxBPFDSLPolicy* policy_;
+ TrapRegistry* registry_;
+
+ Conds conds_;
+ CodeGen gen_;
+ bool has_unsafe_traps_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyCompiler);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
diff --git a/sandbox/linux/bpf_dsl/trap_registry.h b/sandbox/linux/bpf_dsl/trap_registry.h
new file mode 100644
index 0000000..94d4722
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/trap_registry.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
+#define SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This must match the kernel's seccomp_data structure.
+struct arch_seccomp_data {
+ int nr;
+ uint32_t arch;
+ uint64_t instruction_pointer;
+ uint64_t args[6];
+};
+
+namespace bpf_dsl {
+
+// TrapRegistry provides an interface for registering "trap handlers"
+// by associating them with non-zero 16-bit trap IDs. Trap IDs should
+// remain valid for the lifetime of the trap registry.
+class SANDBOX_EXPORT TrapRegistry {
+ public:
+ // TrapFnc is a pointer to a function that fulfills the trap handler
+ // function signature.
+ //
+ // Trap handlers follow the calling convention of native system
+ // calls; e.g., to report an error, they return an exit code in the
+ // range -1..-4096 instead of directly modifying errno. However,
+ // modifying errno is harmless, as the original value will be
+ // restored afterwards.
+ //
+ // Trap handlers are executed from signal context and possibly an
+ // async-signal context, so they must be async-signal safe:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+ typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void* aux);
+
+ // Add registers the specified trap handler tuple and returns a
+ // non-zero trap ID that uniquely identifies the tuple for the life
+ // time of the trap registry. If the same tuple is registered
+ // multiple times, the same value will be returned each time.
+ virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) = 0;
+
+ // EnableUnsafeTraps tries to enable unsafe traps and returns
+ // whether it was successful. This is a one-way operation.
+ virtual bool EnableUnsafeTraps() = 0;
+
+ protected:
+ TrapRegistry() {}
+ ~TrapRegistry() {}
+
+ DISALLOW_COPY_AND_ASSIGN(TrapRegistry);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
new file mode 100644
index 0000000..20dac8f
--- /dev/null
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -0,0 +1,357 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'conditions': [
+ ['OS=="linux"', {
+ 'compile_suid_client': 1,
+ 'compile_credentials': 1,
+ }, {
+ 'compile_suid_client': 0,
+ 'compile_credentials': 0,
+ }],
+ ['OS=="linux" and (target_arch=="ia32" or target_arch=="x64" or '
+ 'target_arch=="mipsel")', {
+ 'compile_seccomp_bpf_demo': 1,
+ }, {
+ 'compile_seccomp_bpf_demo': 0,
+ }],
+ ],
+ },
+ 'target_defaults': {
+ 'target_conditions': [
+ # All linux/ files will automatically be excluded on Android
+ # so make sure we re-include them explicitly.
+ ['OS == "android"', {
+ 'sources/': [
+ ['include', '^linux/'],
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ # We have two principal targets: sandbox and sandbox_linux_unittests
+ # All other targets are listed as dependencies.
+ # There is one notable exception: for historical reasons, chrome_sandbox is
+ # the setuid sandbox and is its own target.
+ {
+ 'target_name': 'sandbox',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_services',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'dependencies': [
+ 'suid_sandbox_client',
+ ],
+ }],
+ # Compile seccomp BPF when we support it.
+ [ 'use_seccomp_bpf==1', {
+ 'dependencies': [
+ 'seccomp_bpf',
+ 'seccomp_bpf_helpers',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sandbox_linux_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'tests/sandbox_test_runner.cc',
+ 'tests/sandbox_test_runner.h',
+ 'tests/sandbox_test_runner_function_pointer.cc',
+ 'tests/sandbox_test_runner_function_pointer.h',
+ 'tests/test_utils.cc',
+ 'tests/test_utils.h',
+ 'tests/unit_tests.cc',
+ 'tests/unit_tests.h',
+ ],
+ 'conditions': [
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'seccomp-bpf/bpf_tester_compatibility_delegate.h',
+ 'seccomp-bpf/bpf_tests.h',
+ 'seccomp-bpf/sandbox_bpf_test_runner.cc',
+ 'seccomp-bpf/sandbox_bpf_test_runner.h',
+ ],
+ 'dependencies': [
+ 'seccomp_bpf',
+ ]
+ }],
+ ],
+ },
+ {
+ # The main sandboxing test target.
+ 'target_name': 'sandbox_linux_unittests',
+ 'includes': [
+ 'sandbox_linux_test_sources.gypi',
+ ],
+ 'type': 'executable',
+ },
+ {
+ # This target is the shared library used by Android APK (i.e.
+ # JNI-friendly) tests.
+ 'target_name': 'sandbox_linux_jni_unittests',
+ 'includes': [
+ 'sandbox_linux_test_sources.gypi',
+ ],
+ 'type': 'shared_library',
+ 'conditions': [
+ [ 'OS == "android"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'seccomp_bpf',
+ 'type': '<(component)',
+ 'sources': [
+ 'bpf_dsl/bpf_dsl.cc',
+ 'bpf_dsl/bpf_dsl.h',
+ 'bpf_dsl/bpf_dsl_impl.h',
+ 'bpf_dsl/cons.h',
+ 'bpf_dsl/policy_compiler.cc',
+ 'bpf_dsl/policy_compiler.h',
+ 'bpf_dsl/trap_registry.h',
+ 'seccomp-bpf/basicblock.cc',
+ 'seccomp-bpf/basicblock.h',
+ 'seccomp-bpf/codegen.cc',
+ 'seccomp-bpf/codegen.h',
+ 'seccomp-bpf/die.cc',
+ 'seccomp-bpf/die.h',
+ 'seccomp-bpf/errorcode.cc',
+ 'seccomp-bpf/errorcode.h',
+ 'seccomp-bpf/instruction.h',
+ 'seccomp-bpf/linux_seccomp.h',
+ 'seccomp-bpf/sandbox_bpf.cc',
+ 'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/syscall.cc',
+ 'seccomp-bpf/syscall.h',
+ 'seccomp-bpf/syscall_iterator.cc',
+ 'seccomp-bpf/syscall_iterator.h',
+ 'seccomp-bpf/trap.cc',
+ 'seccomp-bpf/trap.h',
+ 'seccomp-bpf/verifier.cc',
+ 'seccomp-bpf/verifier.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services_headers',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'includes': [
+ # Disable LTO due to compiler bug
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57703
+ '../../build/android/disable_lto.gypi',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ {
+ 'target_name': 'seccomp_bpf_helpers',
+ 'type': '<(component)',
+ 'sources': [
+ 'seccomp-bpf-helpers/baseline_policy.cc',
+ 'seccomp-bpf-helpers/baseline_policy.h',
+ 'seccomp-bpf-helpers/sigsys_handlers.cc',
+ 'seccomp-bpf-helpers/sigsys_handlers.h',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.h',
+ 'seccomp-bpf-helpers/syscall_sets.cc',
+ 'seccomp-bpf-helpers/syscall_sets.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'seccomp_bpf',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ {
+ # The setuid sandbox, for Linux
+ 'target_name': 'chrome_sandbox',
+ 'type': 'executable',
+ 'sources': [
+ 'suid/common/sandbox.h',
+ 'suid/common/suid_unsafe_environment_variables.h',
+ 'suid/process_util.h',
+ 'suid/process_util_linux.c',
+ 'suid/sandbox.c',
+ ],
+ 'cflags': [
+ # For ULLONG_MAX
+ '-std=gnu99',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ # Do not use any sanitizer tools with this binary. http://crbug.com/382766
+ 'cflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ 'ldflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ },
+ { 'target_name': 'sandbox_services',
+ 'type': '<(component)',
+ 'sources': [
+ 'services/broker_process.cc',
+ 'services/broker_process.h',
+ 'services/init_process_reaper.cc',
+ 'services/init_process_reaper.h',
+ 'services/scoped_process.cc',
+ 'services/scoped_process.h',
+ 'services/thread_helpers.cc',
+ 'services/thread_helpers.h',
+ 'services/yama.h',
+ 'services/yama.cc',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'conditions': [
+ ['compile_credentials==1', {
+ 'sources': [
+ 'services/credentials.cc',
+ 'services/credentials.h',
+ ],
+ 'dependencies': [
+ # for capabilities.cc.
+ '../build/linux/system.gyp:libcap',
+ ],
+ }],
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ { 'target_name': 'sandbox_services_headers',
+ 'type': 'none',
+ 'sources': [
+ 'services/android_arm_ucontext.h',
+ 'services/android_arm64_ucontext.h',
+ 'services/android_futex.h',
+ 'services/android_ucontext.h',
+ 'services/android_i386_ucontext.h',
+ 'services/android_mips_ucontext.h',
+ 'services/arm_linux_syscalls.h',
+ 'services/arm64_linux_syscalls.h',
+ 'services/mips_linux_syscalls.h',
+ 'services/linux_syscalls.h',
+ 'services/x86_32_linux_syscalls.h',
+ 'services/x86_64_linux_syscalls.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ # We make this its own target so that it does not interfere
+ # with our tests.
+ 'target_name': 'libc_urandom_override',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/libc_urandom_override.cc',
+ 'services/libc_urandom_override.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'suid_sandbox_client',
+ 'type': '<(component)',
+ 'sources': [
+ 'suid/common/sandbox.h',
+ 'suid/common/suid_unsafe_environment_variables.h',
+ 'suid/client/setuid_sandbox_client.cc',
+ 'suid/client/setuid_sandbox_client.h',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ ],
+ 'conditions': [
+ [ 'OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_stripped',
+ 'type': 'none',
+ 'dependencies': [ 'sandbox_linux_unittests' ],
+ 'actions': [{
+ 'action_name': 'strip sandbox_linux_unittests',
+ 'inputs': [ '<(PRODUCT_DIR)/sandbox_linux_unittests' ],
+ 'outputs': [ '<(PRODUCT_DIR)/sandbox_linux_unittests_stripped' ],
+ 'action': [ '<(android_strip)', '<@(_inputs)', '-o', '<@(_outputs)' ],
+ }],
+ }
+ ],
+ }],
+ [ 'OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_jni_unittests_apk',
+ 'type': 'none',
+ 'variables': {
+ 'test_suite_name': 'sandbox_linux_jni_unittests',
+ },
+ 'dependencies': [
+ 'sandbox_linux_jni_unittests',
+ ],
+ 'includes': [ '../../build/apk_test.gypi' ],
+ }
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sandbox_linux_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi
new file mode 100644
index 0000000..3bab067
--- /dev/null
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -0,0 +1,56 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Tests need to be compiled in the same link unit, so we have to list them
+# in a separate .gypi file.
+{
+ 'dependencies': [
+ 'sandbox',
+ 'sandbox_linux_test_utils',
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'services/broker_process_unittest.cc',
+ 'services/scoped_process_unittest.cc',
+ 'services/thread_helpers_unittests.cc',
+ 'services/yama_unittests.cc',
+ 'tests/main.cc',
+ 'tests/scoped_temporary_file.cc',
+ 'tests/scoped_temporary_file.h',
+ 'tests/scoped_temporary_file_unittest.cc',
+ 'tests/unit_tests_unittest.cc',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'sources': [
+ 'suid/client/setuid_sandbox_client_unittest.cc',
+ ],
+ }],
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'bpf_dsl/bpf_dsl_more_unittest.cc',
+ 'bpf_dsl/bpf_dsl_unittest.cc',
+ 'bpf_dsl/cons_unittest.cc',
+ 'seccomp-bpf-helpers/baseline_policy_unittest.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc',
+ 'seccomp-bpf/bpf_tests_unittest.cc',
+ 'seccomp-bpf/codegen_unittest.cc',
+ 'seccomp-bpf/errorcode_unittest.cc',
+ 'seccomp-bpf/syscall_iterator_unittest.cc',
+ 'seccomp-bpf/syscall_unittest.cc',
+ ],
+ }],
+ [ 'compile_credentials==1', {
+ 'sources': [
+ 'services/credentials_unittest.cc',
+ 'services/unix_domain_socket_unittest.cc',
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/seccomp-bpf-helpers/DEPS b/sandbox/linux/seccomp-bpf-helpers/DEPS
new file mode 100644
index 0000000..01e1fe2
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+sandbox/linux/bpf_dsl",
+ "+sandbox/linux/services",
+ "+sandbox/linux/seccomp-bpf",
+ "+third_party/lss/linux_syscall_support.h",
+]
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
new file mode 100644
index 0000000..eb2997d
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+// Changing this implementation will have an effect on *all* policies.
+// Currently this means: Renderer/Worker, GPU, Flash and NaCl.
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Arg;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::If;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+namespace {
+
+bool IsBaselinePolicyAllowed(int sysno) {
+ return SyscallSets::IsAllowedAddressSpaceAccess(sysno) ||
+ SyscallSets::IsAllowedBasicScheduler(sysno) ||
+ SyscallSets::IsAllowedEpoll(sysno) ||
+ SyscallSets::IsAllowedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsAllowedFutex(sysno) ||
+ SyscallSets::IsAllowedGeneralIo(sysno) ||
+ SyscallSets::IsAllowedGetOrModifySocket(sysno) ||
+ SyscallSets::IsAllowedGettime(sysno) ||
+ SyscallSets::IsAllowedProcessStartOrDeath(sysno) ||
+ SyscallSets::IsAllowedSignalHandling(sysno) ||
+ SyscallSets::IsGetSimpleId(sysno) ||
+ SyscallSets::IsKernelInternalApi(sysno) ||
+#if defined(__arm__)
+ SyscallSets::IsArmPrivate(sysno) ||
+#endif
+#if defined(__mips__)
+ SyscallSets::IsMipsPrivate(sysno) ||
+#endif
+ SyscallSets::IsAllowedOperationOnFd(sysno);
+}
+
+// System calls that will trigger the crashing SIGSYS handler.
+bool IsBaselinePolicyWatched(int sysno) {
+ return SyscallSets::IsAdminOperation(sysno) ||
+ SyscallSets::IsAdvancedScheduler(sysno) ||
+ SyscallSets::IsAdvancedTimer(sysno) ||
+ SyscallSets::IsAsyncIo(sysno) ||
+ SyscallSets::IsDebug(sysno) ||
+ SyscallSets::IsEventFd(sysno) ||
+ SyscallSets::IsExtendedAttributes(sysno) ||
+ SyscallSets::IsFaNotify(sysno) ||
+ SyscallSets::IsFsControl(sysno) ||
+ SyscallSets::IsGlobalFSViewChange(sysno) ||
+ SyscallSets::IsGlobalProcessEnvironment(sysno) ||
+ SyscallSets::IsGlobalSystemStatus(sysno) ||
+ SyscallSets::IsInotify(sysno) ||
+ SyscallSets::IsKernelModule(sysno) ||
+ SyscallSets::IsKeyManagement(sysno) ||
+ SyscallSets::IsKill(sysno) ||
+ SyscallSets::IsMessageQueue(sysno) ||
+ SyscallSets::IsMisc(sysno) ||
+#if defined(__x86_64__)
+ SyscallSets::IsNetworkSocketInformation(sysno) ||
+#endif
+ SyscallSets::IsNuma(sysno) ||
+ SyscallSets::IsPrctl(sysno) ||
+ SyscallSets::IsProcessGroupOrSession(sysno) ||
+#if defined(__i386__) || defined(__mips__)
+ SyscallSets::IsSocketCall(sysno) ||
+#endif
+#if defined(__arm__)
+ SyscallSets::IsArmPciConfig(sysno) ||
+#endif
+#if defined(__mips__)
+ SyscallSets::IsMipsMisc(sysno) ||
+#endif
+ SyscallSets::IsTimer(sysno);
+}
+
+// |fs_denied_errno| is the errno return for denied filesystem access.
+ResultExpr EvaluateSyscallImpl(int fs_denied_errno,
+ pid_t current_pid,
+ int sysno) {
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ // TCGETS is required by the sanitizers on failure.
+ if (sysno == __NR_ioctl) {
+ return RestrictIoctl();
+ }
+
+ if (sysno == __NR_sched_getaffinity) {
+ return Allow();
+ }
+
+ if (sysno == __NR_sigaltstack) {
+ // Required for better stack overflow detection in ASan. Disallowed in
+ // non-ASan builds.
+ return Allow();
+ }
+#endif // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
+ // defined(MEMORY_SANITIZER)
+
+ if (IsBaselinePolicyAllowed(sysno)) {
+ return Allow();
+ }
+
+#if defined(OS_ANDROID)
+ // Needed for thread creation.
+ if (sysno == __NR_sigaltstack)
+ return Allow();
+#endif
+
+ if (sysno == __NR_clock_gettime) {
+ return RestrictClockID();
+ }
+
+ if (sysno == __NR_clone) {
+ return RestrictCloneToThreadsAndEPERMFork();
+ }
+
+ if (sysno == __NR_fcntl)
+ return RestrictFcntlCommands();
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ if (sysno == __NR_fcntl64)
+ return RestrictFcntlCommands();
+#endif
+
+#if !defined(__aarch64__)
+ // fork() is never used as a system call (clone() is used instead), but we
+ // have seen it in fallback code on Android.
+ if (sysno == __NR_fork) {
+ return Error(EPERM);
+ }
+#endif
+
+ if (sysno == __NR_futex)
+ return RestrictFutex();
+
+ if (sysno == __NR_set_robust_list)
+ return Error(EPERM);
+
+ if (sysno == __NR_getpriority || sysno ==__NR_setpriority)
+ return RestrictGetSetpriority(current_pid);
+
+ if (sysno == __NR_madvise) {
+ // Only allow MADV_DONTNEED (aka MADV_FREE).
+ const Arg<int> advice(2);
+ return If(advice == MADV_DONTNEED, Allow()).Else(Error(EPERM));
+ }
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ if (sysno == __NR_mmap)
+ return RestrictMmapFlags();
+#endif
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ if (sysno == __NR_mmap2)
+ return RestrictMmapFlags();
+#endif
+
+ if (sysno == __NR_mprotect)
+ return RestrictMprotectFlags();
+
+ if (sysno == __NR_prctl)
+ return RestrictPrctl();
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ if (sysno == __NR_socketpair) {
+ // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen.
+ COMPILE_ASSERT(AF_UNIX == PF_UNIX, af_unix_pf_unix_different);
+ const Arg<int> domain(0);
+ return If(domain == AF_UNIX, Allow()).Else(CrashSIGSYS());
+ }
+#endif
+
+ if (SyscallSets::IsKill(sysno)) {
+ return RestrictKillTarget(current_pid, sysno);
+ }
+
+ if (SyscallSets::IsFileSystem(sysno) ||
+ SyscallSets::IsCurrentDirectory(sysno)) {
+ return Error(fs_denied_errno);
+ }
+
+ if (SyscallSets::IsSeccomp(sysno))
+ return Error(EPERM);
+
+ if (SyscallSets::IsAnySystemV(sysno)) {
+ return Error(EPERM);
+ }
+
+ if (SyscallSets::IsUmask(sysno) ||
+ SyscallSets::IsDeniedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsDeniedGetOrModifySocket(sysno) ||
+ SyscallSets::IsProcessPrivilegeChange(sysno)) {
+ return Error(EPERM);
+ }
+
+#if defined(__i386__) || defined(__mips__)
+ if (SyscallSets::IsSocketCall(sysno))
+ return RestrictSocketcallCommand();
+#endif
+
+ if (IsBaselinePolicyWatched(sysno)) {
+ // Previously unseen syscalls. TODO(jln): some of these should
+ // be denied gracefully right away.
+ return CrashSIGSYS();
+ }
+
+ // In any other case crash the program with our SIGSYS handler.
+ return CrashSIGSYS();
+}
+
+} // namespace.
+
+// Unfortunately C++03 doesn't allow delegated constructors.
+// Call other constructor when C++11 lands.
+BaselinePolicy::BaselinePolicy() : BaselinePolicy(EPERM) {}
+
+BaselinePolicy::BaselinePolicy(int fs_denied_errno)
+ : fs_denied_errno_(fs_denied_errno), policy_pid_(syscall(__NR_getpid)) {}
+
+BaselinePolicy::~BaselinePolicy() {
+ // Make sure that this policy is created, used and destroyed by a single
+ // process.
+ DCHECK_EQ(syscall(__NR_getpid), policy_pid_);
+}
+
+ResultExpr BaselinePolicy::EvaluateSyscall(int sysno) const {
+ // Sanity check that we're only called with valid syscall numbers.
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Make sure that this policy is used in the creating process.
+ if (1 == sysno) {
+ DCHECK_EQ(syscall(__NR_getpid), policy_pid_);
+ }
+ return EvaluateSyscallImpl(fs_denied_errno_, policy_pid_, sysno);
+}
+
+ResultExpr BaselinePolicy::InvalidSyscall() const {
+ return CrashSIGSYS();
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
new file mode 100644
index 0000000..6c0772d
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is a helper to build seccomp-bpf policies, i.e. policies for a sandbox
+// that reduces the Linux kernel's attack surface. Given its nature, it doesn't
+// have a clear semantics and is mostly "implementation-defined".
+//
+// This class implements the SandboxBPFDSLPolicy interface with a "baseline"
+// policy for use within Chromium.
+// The "baseline" policy is somewhat arbitrary. All Chromium policies are an
+// alteration of it, and it represents a reasonable common ground to run most
+// code in a sandboxed environment.
+// A baseline policy is only valid for the process for which this object was
+// instantiated (so do not fork() and use it in a child).
+class SANDBOX_EXPORT BaselinePolicy : public bpf_dsl::SandboxBPFDSLPolicy {
+ public:
+ BaselinePolicy();
+ // |fs_denied_errno| is the errno returned when a filesystem access system
+ // call is denied.
+ explicit BaselinePolicy(int fs_denied_errno);
+ virtual ~BaselinePolicy();
+
+ virtual bpf_dsl::ResultExpr EvaluateSyscall(
+ int system_call_number) const override;
+ virtual bpf_dsl::ResultExpr InvalidSyscall() const override;
+ pid_t policy_pid() const { return policy_pid_; }
+
+ private:
+ int fs_denied_errno_;
+
+ // The PID that the policy applies to (should be equal to the current pid).
+ pid_t policy_pid_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaselinePolicy);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
new file mode 100644
index 0000000..a71975b
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -0,0 +1,351 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/futex.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/android_futex.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// |pid| is the return value of a fork()-like call. This
+// makes sure that if fork() succeeded the child exits
+// and the parent waits for it.
+void HandlePostForkReturn(pid_t pid) {
+ const int kChildExitCode = 1;
+ if (pid > 0) {
+ int status = 0;
+ PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kChildExitCode, WEXITSTATUS(status));
+ } else if (pid == 0) {
+ _exit(kChildExitCode);
+ }
+}
+
+// Check that HandlePostForkReturn works.
+TEST(BaselinePolicy, HandlePostForkReturn) {
+ pid_t pid = fork();
+ HandlePostForkReturn(pid);
+}
+
+// This also tests that read(), write() and fstat() are allowed.
+void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) {
+ BPF_ASSERT_LE(0, read_end.get());
+ BPF_ASSERT_LE(0, write_end.get());
+ struct stat stat_buf;
+ int sys_ret = fstat(read_end.get(), &stat_buf);
+ BPF_ASSERT_EQ(0, sys_ret);
+ BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode));
+
+ const ssize_t kTestTransferSize = 4;
+ static const char kTestString[kTestTransferSize] = {'T', 'E', 'S', 'T'};
+ ssize_t transfered = 0;
+
+ transfered =
+ HANDLE_EINTR(write(write_end.get(), kTestString, kTestTransferSize));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ char read_buf[kTestTransferSize + 1] = {0};
+ transfered = HANDLE_EINTR(read(read_end.get(), read_buf, sizeof(read_buf)));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ BPF_ASSERT_EQ(0, memcmp(kTestString, read_buf, kTestTransferSize));
+}
+
+// Test that a few easy-to-test system calls are allowed.
+BPF_TEST_C(BaselinePolicy, BaselinePolicyBasicAllowed, BaselinePolicy) {
+ BPF_ASSERT_EQ(0, sched_yield());
+
+ int pipefd[2];
+ int sys_ret = pipe(pipefd);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(pipefd[0]), base::ScopedFD(pipefd[1]));
+
+ BPF_ASSERT_LE(1, getpid());
+ BPF_ASSERT_LE(0, getuid());
+}
+
+BPF_TEST_C(BaselinePolicy, FchmodErrno, BaselinePolicy) {
+ int ret = fchmod(-1, 07777);
+ BPF_ASSERT_EQ(-1, ret);
+ // Without the sandbox, this would EBADF instead.
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkErrno, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = fork();
+ const int fork_errno = errno;
+ HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkX86Glibc() {
+ return syscall(__NR_clone, CLONE_PARENT_SETTID | SIGCHLD);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkX86Glibc();
+ const int fork_errno = errno;
+ HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkARMGlibc() {
+ return syscall(__NR_clone,
+ CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkARMGlibc();
+ const int fork_errno = errno;
+ HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) {
+ base::Thread thread("sandbox_tests");
+ BPF_ASSERT(thread.Start());
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedCloneFlagCrashes,
+ DEATH_SEGV_MESSAGE(GetCloneErrorMessageContentForTests()),
+ BaselinePolicy) {
+ pid_t pid = syscall(__NR_clone, CLONE_THREAD | SIGCHLD);
+ HandlePostForkReturn(pid);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedKillCrashes,
+ DEATH_SEGV_MESSAGE(GetKillErrorMessageContentForTests()),
+ BaselinePolicy) {
+ BPF_ASSERT_NE(1, getpid());
+ kill(1, 0);
+ _exit(0);
+}
+
+BPF_TEST_C(BaselinePolicy, CanKillSelf, BaselinePolicy) {
+ int sys_ret = kill(getpid(), 0);
+ BPF_ASSERT_EQ(0, sys_ret);
+}
+
+BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) {
+ int sv[2];
+ int sys_ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+
+ sys_ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+}
+
+// Not all architectures can restrict the domain for socketpair().
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SocketpairWrongDomain,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ int sv[2];
+ ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv));
+ _exit(1);
+}
+#endif // defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+
+BPF_TEST_C(BaselinePolicy, EPERM_open, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = open("/proc/cpuinfo", O_RDONLY);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_access, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = access("/proc/cpuinfo", R_OK);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) {
+ errno = 0;
+ char buf[1024];
+ char* cwd = getcwd(buf, sizeof(buf));
+ BPF_ASSERT_EQ(NULL, cwd);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SIGSYS_InvalidSyscall,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ Syscall::InvalidCall();
+}
+
+// A failing test using this macro could be problematic since we perform
+// system calls by passing "0" as every argument.
+// The kernel could SIGSEGV the process or the system call itself could reboot
+// the machine. Some thoughts have been given when hand-picking the system
+// calls below to limit any potential side effects outside of the current
+// process.
+#define TEST_BASELINE_SIGSYS(sysno) \
+ BPF_DEATH_TEST_C(BaselinePolicy, \
+ SIGSYS_##sysno, \
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), \
+ BaselinePolicy) { \
+ syscall(sysno, 0, 0, 0, 0, 0, 0); \
+ _exit(1); \
+ }
+
+TEST_BASELINE_SIGSYS(__NR_acct);
+TEST_BASELINE_SIGSYS(__NR_chroot);
+TEST_BASELINE_SIGSYS(__NR_fanotify_init);
+TEST_BASELINE_SIGSYS(__NR_fgetxattr);
+TEST_BASELINE_SIGSYS(__NR_getcpu);
+TEST_BASELINE_SIGSYS(__NR_getitimer);
+TEST_BASELINE_SIGSYS(__NR_init_module);
+TEST_BASELINE_SIGSYS(__NR_io_cancel);
+TEST_BASELINE_SIGSYS(__NR_keyctl);
+TEST_BASELINE_SIGSYS(__NR_mq_open);
+TEST_BASELINE_SIGSYS(__NR_ptrace);
+TEST_BASELINE_SIGSYS(__NR_sched_setaffinity);
+TEST_BASELINE_SIGSYS(__NR_setpgid);
+TEST_BASELINE_SIGSYS(__NR_swapon);
+TEST_BASELINE_SIGSYS(__NR_sysinfo);
+TEST_BASELINE_SIGSYS(__NR_syslog);
+TEST_BASELINE_SIGSYS(__NR_timer_create);
+
+#if !defined(__aarch64__)
+TEST_BASELINE_SIGSYS(__NR_eventfd);
+TEST_BASELINE_SIGSYS(__NR_inotify_init);
+TEST_BASELINE_SIGSYS(__NR_vserver);
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritence,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritencePrivate,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithUnlockPIPrivate,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_UNLOCK_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, PrctlDumpable, BaselinePolicy) {
+ const int is_dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+ BPF_ASSERT(is_dumpable == 1 || is_dumpable == 0);
+ const int prctl_ret = prctl(PR_SET_DUMPABLE, is_dumpable, 0, 0, 0, 0);
+ BPF_ASSERT_EQ(0, prctl_ret);
+}
+
+// Workaround incomplete Android headers.
+#if !defined(PR_CAPBSET_READ)
+#define PR_CAPBSET_READ 23
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ PrctlSigsys,
+ DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()),
+ BaselinePolicy) {
+ prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, GetOrSetPriority, BaselinePolicy) {
+ errno = 0;
+ const int original_prio = getpriority(PRIO_PROCESS, 0);
+ // Check errno instead of the return value since this system call can return
+ // -1 as a valid value.
+ BPF_ASSERT_EQ(0, errno);
+
+ errno = 0;
+ int rc = getpriority(PRIO_PROCESS, getpid());
+ BPF_ASSERT_EQ(0, errno);
+
+ rc = getpriority(PRIO_PROCESS, getpid() + 1);
+ BPF_ASSERT_EQ(-1, rc);
+ BPF_ASSERT_EQ(EPERM, errno);
+
+ rc = setpriority(PRIO_PROCESS, 0, original_prio);
+ BPF_ASSERT_EQ(0, rc);
+
+ rc = setpriority(PRIO_PROCESS, getpid(), original_prio);
+ BPF_ASSERT_EQ(0, rc);
+
+ errno = 0;
+ rc = setpriority(PRIO_PROCESS, getpid() + 1, original_prio);
+ BPF_ASSERT_EQ(-1, rc);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ GetPrioritySigsys,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ getpriority(PRIO_USER, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ ClockGettimeWithDisallowedClockCrashes,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
new file mode 100644
index 0000000..26a1563
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: any code in this file MUST be async-signal safe.
+
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+#if defined(__mips__)
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+#endif
+
+#define SECCOMP_MESSAGE_COMMON_CONTENT "seccomp-bpf failure"
+#define SECCOMP_MESSAGE_CLONE_CONTENT "clone() failure"
+#define SECCOMP_MESSAGE_PRCTL_CONTENT "prctl() failure"
+#define SECCOMP_MESSAGE_IOCTL_CONTENT "ioctl() failure"
+#define SECCOMP_MESSAGE_KILL_CONTENT "(tg)kill() failure"
+#define SECCOMP_MESSAGE_FUTEX_CONTENT "futex() failure"
+
+namespace {
+
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+// Write |error_message| to stderr. Similar to RawLog(), but a bit more careful
+// about async-signal safety. |size| is the size to write and should typically
+// not include a terminating \0.
+void WriteToStdErr(const char* error_message, size_t size) {
+ while (size > 0) {
+ // TODO(jln): query the current policy to check if send() is available and
+ // use it to perform a non-blocking write.
+ const int ret = HANDLE_EINTR(write(STDERR_FILENO, error_message, size));
+ // We can't handle any type of error here.
+ if (ret <= 0 || static_cast<size_t>(ret) > size) break;
+ size -= ret;
+ error_message += ret;
+ }
+}
+
+// Invalid syscall values are truncated to zero.
+// On architectures where base value is zero (Intel and Arm),
+// syscall number is the same as offset from base.
+// This function returns values between 0 and 1023 on all architectures.
+// On architectures where base value is different than zero (currently only
+// Mips), we are truncating valid syscall values to offset from base.
+uint32_t SyscallNumberToOffsetFromBase(uint32_t sysno) {
+#if defined(__mips__)
+ // On MIPS syscall numbers are in different range than on x86 and ARM.
+ // Valid MIPS O32 ABI syscall __NR_syscall will be truncated to zero for
+ // simplicity.
+ sysno = sysno - __NR_Linux;
+#endif
+
+ if (sysno >= 1024)
+ sysno = 0;
+
+ return sysno;
+}
+
+// Print a seccomp-bpf failure to handle |sysno| to stderr in an
+// async-signal safe way.
+void PrintSyscallError(uint32_t sysno) {
+ if (sysno >= 1024)
+ sysno = 0;
+ // TODO(markus): replace with async-signal safe snprintf when available.
+ const size_t kNumDigits = 4;
+ char sysno_base10[kNumDigits];
+ uint32_t rem = sysno;
+ uint32_t mod = 0;
+ for (int i = kNumDigits - 1; i >= 0; i--) {
+ mod = rem % 10;
+ rem /= 10;
+ sysno_base10[i] = '0' + mod;
+ }
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+ static const char kSeccompErrorPrefix[] = __FILE__
+ ":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall 4000 + ";
+#else
+ static const char kSeccompErrorPrefix[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall ";
+#endif
+ static const char kSeccompErrorPostfix[] = "\n";
+ WriteToStdErr(kSeccompErrorPrefix, sizeof(kSeccompErrorPrefix) - 1);
+ WriteToStdErr(sysno_base10, sizeof(sysno_base10));
+ WriteToStdErr(kSeccompErrorPostfix, sizeof(kSeccompErrorPostfix) - 1);
+}
+
+} // namespace.
+
+namespace sandbox {
+
+intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {
+ uint32_t syscall = SyscallNumberToOffsetFromBase(args.nr);
+
+ PrintSyscallError(syscall);
+
+ // Encode 8-bits of the 1st two arguments too, so we can discern which socket
+ // type, which fcntl, ... etc., without being likely to hit a mapped
+ // address.
+ // Do not encode more bits here without thinking about increasing the
+ // likelihood of collision with mapped pages.
+ syscall |= ((args.args[0] & 0xffUL) << 12);
+ syscall |= ((args.args[1] & 0xffUL) << 20);
+ // Purposefully dereference the syscall as an address so it'll show up very
+ // clearly and easily in crash dumps.
+ volatile char* addr = reinterpret_cast<volatile char*>(syscall);
+ *addr = '\0';
+ // In case we hit a mapped address, hit the null page with just the syscall,
+ // for paranoia.
+ syscall &= 0xfffUL;
+ addr = reinterpret_cast<volatile char*>(syscall);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+// TODO(jln): refactor the reporting functions.
+
+intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
+ static const char kSeccompCloneError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_CLONE_CONTENT "\n";
+ WriteToStdErr(kSeccompCloneError, sizeof(kSeccompCloneError) - 1);
+ // "flags" is the first argument in the kernel's clone().
+ // Mark as volatile to be able to find the value on the stack in a minidump.
+ volatile uint64_t clone_flags = args.args[0];
+ volatile char* addr;
+ if (IsArchitectureX86_64()) {
+ addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFFFFF);
+ *addr = '\0';
+ }
+ // Hit the NULL page if this fails to fault.
+ addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompPrctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_PRCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompPrctlError, sizeof(kSeccompPrctlError) - 1);
+ // Mark as volatile to be able to find the value on the stack in a minidump.
+ volatile uint64_t option = args.args[0];
+ volatile char* addr =
+ reinterpret_cast<volatile char*>(option & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompIoctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_IOCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompIoctlError, sizeof(kSeccompIoctlError) - 1);
+ // Make "request" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t request = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(request & 0xFFFF);
+ *addr = '\0';
+ // Hit the NULL page if this fails.
+ addr = reinterpret_cast<volatile char*>(request & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSKillFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompKillError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_KILL_CONTENT "\n";
+ WriteToStdErr(kSeccompKillError, sizeof(kSeccompKillError) - 1);
+ // Make "request" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t pid = args.args[0];
+ volatile char* addr = reinterpret_cast<volatile char*>(pid & 0xFFF);
+ *addr = '\0';
+ // Hit the NULL page if this fails.
+ addr = reinterpret_cast<volatile char*>(pid & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompFutexError[] =
+ __FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_FUTEX_CONTENT "\n";
+ WriteToStdErr(kSeccompFutexError, sizeof(kSeccompFutexError) - 1);
+ volatile int futex_op = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(futex_op & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ switch (args.nr) {
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler:
+ const pid_t tid = syscall(__NR_gettid);
+ // The first argument is the pid. If is our thread id, then replace it
+ // with 0, which is equivalent and allowed by the policy.
+ if (args.args[0] == static_cast<uint64_t>(tid)) {
+ return Syscall::Call(args.nr,
+ 0,
+ static_cast<intptr_t>(args.args[1]),
+ static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]),
+ static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
+ }
+ break;
+ }
+
+ CrashSIGSYS_Handler(args, aux);
+
+ // Should never be reached.
+ RAW_CHECK(false);
+ return -ENOSYS;
+}
+
+bpf_dsl::ResultExpr CrashSIGSYS() {
+ return bpf_dsl::Trap(CrashSIGSYS_Handler, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSClone() {
+ return bpf_dsl::Trap(SIGSYSCloneFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSPrctl() {
+ return bpf_dsl::Trap(SIGSYSPrctlFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSIoctl() {
+ return bpf_dsl::Trap(SIGSYSIoctlFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSKill() {
+ return bpf_dsl::Trap(SIGSYSKillFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSFutex() {
+ return bpf_dsl::Trap(SIGSYSFutexFailure, NULL);
+}
+
+bpf_dsl::ResultExpr RewriteSchedSIGSYS() {
+ return bpf_dsl::Trap(SIGSYSSchedHandler, NULL);
+}
+
+const char* GetErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_COMMON_CONTENT;
+}
+
+const char* GetCloneErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_CLONE_CONTENT;
+}
+
+const char* GetPrctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_PRCTL_CONTENT;
+}
+
+const char* GetIoctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_IOCTL_CONTENT;
+}
+
+const char* GetKillErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_KILL_CONTENT;
+}
+
+const char* GetFutexErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_FUTEX_CONTENT;
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
new file mode 100644
index 0000000..31f3020
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/sandbox_export.h"
+
+// The handlers are suitable for use in Trap() error codes. They are
+// guaranteed to be async-signal safe.
+// See sandbox/linux/seccomp-bpf/trap.h to see how they work.
+
+namespace sandbox {
+
+struct arch_seccomp_data;
+
+// This handler will crash the currently running process. The crashing address
+// will be the number of the current system call, extracted from |args|.
+// This handler will also print to stderr the number of the crashing syscall.
+SANDBOX_EXPORT intptr_t
+ CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux);
+
+// The following three handlers are suitable to report failures with the
+// clone(), prctl() and ioctl() system calls respectively.
+
+// The crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is
+// the clone(2) argument, extracted from |args|.
+SANDBOX_EXPORT intptr_t
+ SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (option & 0xFFF), where option is the prctl(2)
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSPrctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be request & 0xFFFF, where request is the ioctl(2)
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSIoctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (pid & 0xFFF), where pid is the first
+// argument (and can be a tid).
+SANDBOX_EXPORT intptr_t
+ SIGSYSKillFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (op & 0xFFF), where op is the second
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
+// If the syscall is not being called on the current tid, crashes in the same
+// way as CrashSIGSYS_Handler. Otherwise, returns the result of calling the
+// syscall with the pid argument set to 0 (which for these calls means the
+// current thread). The following syscalls are supported:
+//
+// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
+// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
+// sched_setparam(), sched_setscheduler()
+SANDBOX_EXPORT intptr_t
+ SIGSYSSchedHandler(const struct arch_seccomp_data& args, void* aux);
+
+// Variants of the above functions for use with bpf_dsl.
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYS();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSClone();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPrctl();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSIoctl();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex();
+SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS();
+
+// Following four functions return substrings of error messages used
+// in the above four functions. They are useful in death tests.
+SANDBOX_EXPORT const char* GetErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetCloneErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetPrctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetIoctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetKillErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetFutexErrorMessageContentForTests();
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
new file mode 100644
index 0000000..de59dd8
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fcntl.h>
+#include <linux/futex.h>
+#include <linux/net.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+#if defined(OS_ANDROID)
+
+#include "sandbox/linux/services/android_futex.h"
+
+#if !defined(F_DUPFD_CLOEXEC)
+#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
+#endif
+
+#endif // defined(OS_ANDROID)
+
+#if defined(__arm__) && !defined(MAP_STACK)
+#define MAP_STACK 0x20000 // Daisy build environment has old headers.
+#endif
+
+#if defined(__mips__) && !defined(MAP_STACK)
+#define MAP_STACK 0x40000
+#endif
+namespace {
+
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsArchitectureI386() {
+#if defined(__i386__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsAndroid() {
+#if defined(OS_ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsArchitectureMips() {
+#if defined(__mips__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // namespace.
+
+#define CASES SANDBOX_BPF_DSL_CASES
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Arg;
+using sandbox::bpf_dsl::BoolExpr;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::If;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+// Allow Glibc's and Android pthread creation flags, crash on any other
+// thread creation attempts and EPERM attempts to use neither
+// CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations.
+ResultExpr RestrictCloneToThreadsAndEPERMFork() {
+ const Arg<unsigned long> flags(0);
+
+ // TODO(mdempsky): Extend DSL to support (flags & ~mask1) == mask2.
+ const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const uint64_t kObsoleteAndroidCloneMask = kAndroidCloneMask | CLONE_DETACHED;
+
+ const uint64_t kGlibcPthreadFlags =
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const BoolExpr glibc_test = flags == kGlibcPthreadFlags;
+
+ const BoolExpr android_test = flags == kAndroidCloneMask ||
+ flags == kObsoleteAndroidCloneMask ||
+ flags == kGlibcPthreadFlags;
+
+ return If(IsAndroid() ? android_test : glibc_test, Allow())
+ .ElseIf((flags & (CLONE_VM | CLONE_THREAD)) == 0, Error(EPERM))
+ .Else(CrashSIGSYSClone());
+}
+
+ResultExpr RestrictPrctl() {
+ // Will need to add seccomp compositing in the future. PR_SET_PTRACER is
+ // used by breakpad but not needed anymore.
+ const Arg<int> option(0);
+ return Switch(option)
+ .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE),
+ Allow())
+ .Default(CrashSIGSYSPrctl());
+}
+
+ResultExpr RestrictIoctl() {
+ const Arg<int> request(1);
+ return Switch(request).CASES((TCGETS, FIONREAD), Allow()).Default(
+ CrashSIGSYSIoctl());
+}
+
+ResultExpr RestrictMmapFlags() {
+ // The flags you see are actually the allowed ones, and the variable is a
+ // "denied" mask because of the negation operator.
+ // Significantly, we don't permit MAP_HUGETLB, or the newer flags such as
+ // MAP_POPULATE.
+ // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries.
+ const uint64_t kAllowedMask = MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
+ MAP_STACK | MAP_NORESERVE | MAP_FIXED |
+ MAP_DENYWRITE;
+ const Arg<int> flags(3);
+ return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictMprotectFlags() {
+ // The flags you see are actually the allowed ones, and the variable is a
+ // "denied" mask because of the negation operator.
+ // Significantly, we don't permit weird undocumented flags such as
+ // PROT_GROWSDOWN.
+ const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC;
+ const Arg<int> prot(2);
+ return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictFcntlCommands() {
+ // We also restrict the flags in F_SETFL. We don't want to permit flags with
+ // a history of trouble such as O_DIRECT. The flags you see are actually the
+ // allowed ones, and the variable is a "denied" mask because of the negation
+ // operator.
+ // Glibc overrides the kernel's O_LARGEFILE value. Account for this.
+ uint64_t kOLargeFileFlag = O_LARGEFILE;
+ if (IsArchitectureX86_64() || IsArchitectureI386() || IsArchitectureMips())
+ kOLargeFileFlag = 0100000;
+
+ const Arg<int> cmd(1);
+ const Arg<long> long_arg(2);
+
+ const uint64_t kAllowedMask = O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC |
+ kOLargeFileFlag | O_CLOEXEC | O_NOATIME;
+ return Switch(cmd)
+ .CASES((F_GETFL,
+ F_GETFD,
+ F_SETFD,
+ F_SETLK,
+ F_SETLKW,
+ F_GETLK,
+ F_DUPFD,
+ F_DUPFD_CLOEXEC),
+ Allow())
+ .Case(F_SETFL,
+ If((long_arg & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()))
+ .Default(CrashSIGSYS());
+}
+
+#if defined(__i386__) || defined(__mips__)
+ResultExpr RestrictSocketcallCommand() {
+ // Unfortunately, we are unable to restrict the first parameter to
+ // socketpair(2). Whilst initially sounding bad, it's noteworthy that very
+ // few protocols actually support socketpair(2). The scary call that we're
+ // worried about, socket(2), remains blocked.
+ const Arg<int> call(0);
+ return Switch(call)
+ .CASES((SYS_SOCKETPAIR,
+ SYS_SHUTDOWN,
+ SYS_RECV,
+ SYS_SEND,
+ SYS_RECVFROM,
+ SYS_SENDTO,
+ SYS_RECVMSG,
+ SYS_SENDMSG),
+ Allow())
+ .Default(Error(EPERM));
+}
+#endif
+
+ResultExpr RestrictKillTarget(pid_t target_pid, int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill: {
+ const Arg<pid_t> pid(0);
+ return If(pid == target_pid, Allow()).Else(CrashSIGSYSKill());
+ }
+ case __NR_tkill:
+ return CrashSIGSYSKill();
+ default:
+ NOTREACHED();
+ return CrashSIGSYS();
+ }
+}
+
+ResultExpr RestrictFutex() {
+ const uint64_t kAllowedFutexFlags = FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME;
+ const Arg<int> op(1);
+ return Switch(op & ~kAllowedFutexFlags)
+ .CASES((FUTEX_WAIT,
+ FUTEX_WAKE,
+ FUTEX_REQUEUE,
+ FUTEX_CMP_REQUEUE,
+ FUTEX_WAKE_OP,
+ FUTEX_WAIT_BITSET,
+ FUTEX_WAKE_BITSET),
+ Allow())
+ .Default(CrashSIGSYSFutex());
+}
+
+ResultExpr RestrictGetSetpriority(pid_t target_pid) {
+ const Arg<int> which(0);
+ const Arg<int> who(1);
+ return If(which == PRIO_PROCESS,
+ If(who == 0 || who == target_pid, Allow()).Else(Error(EPERM)))
+ .Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictClockID() {
+ COMPILE_ASSERT(4 == sizeof(clockid_t), clockid_is_not_32bit);
+ const Arg<clockid_t> clockid(0);
+ return If(
+#if defined(OS_CHROMEOS)
+ // Allow the special clock for Chrome OS used by Chrome tracing.
+ clockid == base::TimeTicks::kClockSystemTrace ||
+#endif
+ clockid == CLOCK_MONOTONIC ||
+ clockid == CLOCK_PROCESS_CPUTIME_ID ||
+ clockid == CLOCK_REALTIME ||
+ clockid == CLOCK_THREAD_CPUTIME_ID,
+ Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictSchedTarget(pid_t target_pid, int sysno) {
+ switch (sysno) {
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler: {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0 || pid == target_pid, Allow())
+ .Else(RewriteSchedSIGSYS());
+ }
+ default:
+ NOTREACHED();
+ return CrashSIGSYS();
+ }
+}
+
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
new file mode 100644
index 0000000..bf9909c
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+
+#include <unistd.h>
+
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/sandbox_export.h"
+
+// These are helpers to build seccomp-bpf policies, i.e. policies for a
+// sandbox that reduces the Linux kernel's attack surface. They return a
+// bpf_dsl::ResultExpr suitable to restrict certain system call parameters.
+
+namespace sandbox {
+
+// Allow clone(2) for threads.
+// Reject fork(2) attempts with EPERM.
+// Don't restrict on ASAN.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictCloneToThreadsAndEPERMFork();
+
+// Allow PR_SET_NAME, PR_SET_DUMPABLE, PR_GET_DUMPABLE.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrctl();
+
+// Allow TCGETS and FIONREAD.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictIoctl();
+
+// Restrict the flags argument in mmap(2).
+// Only allow: MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
+// MAP_STACK | MAP_NORESERVE | MAP_FIXED | MAP_DENYWRITE.
+// Crash if any other flag is used.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMmapFlags();
+
+// Restrict the prot argument in mprotect(2).
+// Only allow: PROT_READ | PROT_WRITE | PROT_EXEC.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMprotectFlags();
+
+// Restrict fcntl(2) cmd argument to:
+// We allow F_GETFL, F_SETFL, F_GETFD, F_SETFD, F_DUPFD, F_DUPFD_CLOEXEC,
+// F_SETLK, F_SETLKW and F_GETLK.
+// Also, in F_SETFL, restrict the allowed flags to: O_ACCMODE | O_APPEND |
+// O_NONBLOCK | O_SYNC | O_LARGEFILE | O_CLOEXEC | O_NOATIME.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictFcntlCommands();
+
+#if defined(__i386__) || defined(__mips__)
+// Restrict socketcall(2) to only allow socketpair(2), send(2), recv(2),
+// sendto(2), recvfrom(2), shutdown(2), sendmsg(2) and recvmsg(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSocketcallCommand();
+#endif
+
+// Restrict |sysno| (which must be kill, tkill or tgkill) by allowing tgkill or
+// kill iff the first parameter is |target_pid|, crashing otherwise or if
+// |sysno| is tkill.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictKillTarget(pid_t target_pid,
+ int sysno);
+
+// Crash if FUTEX_CMP_REQUEUE_PI is used in the second argument of futex(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictFutex();
+
+// Crash if |which| is not PRIO_PROCESS. EPERM if |who| is not 0, neither
+// |target_pid| while calling setpriority(2) / getpriority(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetSetpriority(pid_t target_pid);
+
+// Restrict |clk_id| for clock_getres(), clock_gettime() and clock_settime().
+// We allow accessing only CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID,
+// CLOCK_REALTIME, and CLOCK_THREAD_CPUTIME_ID. In particular, this disallows
+// access to arbitrary per-{process,thread} CPU-time clock IDs (such as those
+// returned by {clock,pthread}_getcpuclockid), which can leak information
+// about the state of the host OS.
+// On Chrome OS, base::TimeTicks::kClockSystemTrace is also allowed.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictClockID();
+
+// Restricts |pid| for sched_* syscalls which take a pid as the first argument.
+// We only allow calling these syscalls if the pid argument is equal to the pid
+// of the sandboxed process or 0 (indicating the current thread). The following
+// syscalls are supported:
+//
+// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
+// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
+// sched_setparam(), sched_setscheduler()
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSchedTarget(pid_t target_pid,
+ int sysno);
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
new file mode 100644
index 0000000..c865e7e
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -0,0 +1,215 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/sys_info.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+#if !defined(OS_ANDROID)
+#include "third_party/lss/linux_syscall_support.h" // for MAKE_PROCESS_CPUCLOCK
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// NOTE: most of the parameter restrictions are tested in
+// baseline_policy_unittest.cc as a more end-to-end test.
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::SandboxBPFDSLPolicy;
+
+class RestrictClockIdPolicy : public SandboxBPFDSLPolicy {
+ public:
+ RestrictClockIdPolicy() {}
+ virtual ~RestrictClockIdPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_clock_gettime:
+ case __NR_clock_getres:
+ return RestrictClockID();
+ default:
+ return Allow();
+ }
+ }
+};
+
+void CheckClock(clockid_t clockid) {
+ struct timespec ts;
+ ts.tv_sec = ts.tv_nsec = -1;
+ BPF_ASSERT_EQ(0, clock_gettime(clockid, &ts));
+ BPF_ASSERT_LE(0, ts.tv_sec);
+ BPF_ASSERT_LE(0, ts.tv_nsec);
+}
+
+BPF_TEST_C(ParameterRestrictions,
+ clock_gettime_allowed,
+ RestrictClockIdPolicy) {
+ CheckClock(CLOCK_MONOTONIC);
+ CheckClock(CLOCK_PROCESS_CPUTIME_ID);
+ CheckClock(CLOCK_REALTIME);
+ CheckClock(CLOCK_THREAD_CPUTIME_ID);
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_monotonic_raw,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+}
+
+#if defined(OS_CHROMEOS)
+
+// A custom BPF tester delegate to run IsRunningOnChromeOS() before
+// the sandbox is enabled because we cannot run it with non-SFI BPF
+// sandbox enabled.
+class ClockSystemTesterDelegate : public sandbox::BPFTesterDelegate {
+ public:
+ ClockSystemTesterDelegate()
+ : is_running_on_chromeos_(base::SysInfo::IsRunningOnChromeOS()) {}
+ virtual ~ClockSystemTesterDelegate() {}
+
+ virtual scoped_ptr<sandbox::bpf_dsl::SandboxBPFDSLPolicy>
+ GetSandboxBPFPolicy() override {
+ return scoped_ptr<sandbox::bpf_dsl::SandboxBPFDSLPolicy>(
+ new RestrictClockIdPolicy());
+ }
+ virtual void RunTestFunction() override {
+ if (is_running_on_chromeos_) {
+ CheckClock(base::TimeTicks::kClockSystemTrace);
+ } else {
+ struct timespec ts;
+ // kClockSystemTrace is 11, which is CLOCK_THREAD_CPUTIME_ID of
+ // the init process (pid=1). If kernel supports this feature,
+ // this may succeed even if this is not running on Chrome OS. We
+ // just check this clock_gettime call does not crash.
+ clock_gettime(base::TimeTicks::kClockSystemTrace, &ts);
+ }
+ }
+
+ private:
+ const bool is_running_on_chromeos_;
+ DISALLOW_COPY_AND_ASSIGN(ClockSystemTesterDelegate);
+};
+
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, ClockSystemTesterDelegate);
+
+#elif defined(OS_LINUX)
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_system_trace,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ struct timespec ts;
+ clock_gettime(base::TimeTicks::kClockSystemTrace, &ts);
+}
+
+#endif // defined(OS_CHROMEOS)
+
+#if !defined(OS_ANDROID)
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_cpu_clock,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ // We can't use clock_getcpuclockid() because it's not implemented in newlib,
+ // and it might not work inside the sandbox anyway.
+ const pid_t kInitPID = 1;
+ const clockid_t kInitCPUClockID =
+ MAKE_PROCESS_CPUCLOCK(kInitPID, CPUCLOCK_SCHED);
+
+ struct timespec ts;
+ clock_gettime(kInitCPUClockID, &ts);
+}
+#endif // !defined(OS_ANDROID)
+
+class RestrictSchedPolicy : public SandboxBPFDSLPolicy {
+ public:
+ RestrictSchedPolicy() {}
+ virtual ~RestrictSchedPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_sched_getparam:
+ return RestrictSchedTarget(getpid(), sysno);
+ default:
+ return Allow();
+ }
+ }
+};
+
+void CheckSchedGetParam(pid_t pid, struct sched_param* param) {
+ BPF_ASSERT_EQ(0, sched_getparam(pid, param));
+}
+
+void SchedGetParamThread(base::WaitableEvent* thread_run) {
+ const pid_t pid = getpid();
+ const pid_t tid = syscall(__NR_gettid);
+ BPF_ASSERT_NE(pid, tid);
+
+ struct sched_param current_pid_param;
+ CheckSchedGetParam(pid, ¤t_pid_param);
+
+ struct sched_param zero_param;
+ CheckSchedGetParam(0, &zero_param);
+
+ struct sched_param tid_param;
+ CheckSchedGetParam(tid, &tid_param);
+
+ BPF_ASSERT_EQ(zero_param.sched_priority, tid_param.sched_priority);
+
+ // Verify that the SIGSYS handler sets errno properly.
+ errno = 0;
+ BPF_ASSERT_EQ(-1, sched_getparam(tid, NULL));
+ BPF_ASSERT_EQ(EINVAL, errno);
+
+ thread_run->Signal();
+}
+
+BPF_TEST_C(ParameterRestrictions,
+ sched_getparam_allowed,
+ RestrictSchedPolicy) {
+ base::WaitableEvent thread_run(true, false);
+ // Run the actual test in a new thread so that the current pid and tid are
+ // different.
+ base::Thread getparam_thread("sched_getparam_thread");
+ BPF_ASSERT(getparam_thread.Start());
+ getparam_thread.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&SchedGetParamThread, &thread_run));
+ BPF_ASSERT(thread_run.TimedWait(base::TimeDelta::FromMilliseconds(5000)));
+ getparam_thread.Stop();
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ sched_getparam_crash_non_zero,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictSchedPolicy) {
+ const pid_t kInitPID = 1;
+ struct sched_param param;
+ sched_getparam(kInitPID, ¶m);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
new file mode 100644
index 0000000..640be69
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -0,0 +1,1060 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
+
+#include "build/build_config.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+namespace sandbox {
+
+// The functions below cover all existing i386, x86_64, and ARM system calls;
+// excluding syscalls made obsolete in ARM EABI.
+// The implicitly defined sets form a partition of the sets of
+// system calls.
+
+bool SyscallSets::IsKill(int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill:
+ case __NR_tkill: // Deprecated.
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGettime(int sysno) {
+ switch (sysno) {
+ case __NR_gettimeofday:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_time:
+#endif
+ return true;
+ case __NR_adjtimex: // Privileged.
+ case __NR_clock_adjtime: // Privileged.
+ case __NR_clock_getres: // Could be allowed.
+ case __NR_clock_gettime:
+ case __NR_clock_nanosleep: // Could be allowed.
+ case __NR_clock_settime: // Privileged.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_ftime: // Obsolete.
+#endif
+ case __NR_settimeofday: // Privileged.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_stime:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsCurrentDirectory(int sysno) {
+ switch (sysno) {
+ case __NR_getcwd:
+ case __NR_chdir:
+ case __NR_fchdir:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsUmask(int sysno) {
+ switch (sysno) {
+ case __NR_umask:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// System calls that directly access the file system. They might acquire
+// a new file descriptor or otherwise perform an operation directly
+// via a path.
+// Both EPERM and ENOENT are valid errno unless otherwise noted in comment.
+bool SyscallSets::IsFileSystem(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_access: // EPERM not a valid errno.
+ case __NR_chmod:
+ case __NR_chown:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_chown32:
+#endif
+ case __NR_creat:
+ case __NR_futimesat: // Should be called utimesat ?
+ case __NR_lchown:
+ case __NR_link:
+ case __NR_lstat: // EPERM not a valid errno.
+ case __NR_mkdir:
+ case __NR_mknod:
+ case __NR_open:
+ case __NR_readlink: // EPERM not a valid errno.
+ case __NR_rename:
+ case __NR_rmdir:
+ case __NR_stat: // EPERM not a valid errno.
+ case __NR_symlink:
+ case __NR_unlink:
+ case __NR_uselib: // Neither EPERM, nor ENOENT are valid errno.
+ case __NR_ustat: // Same as above. Deprecated.
+ case __NR_utimes:
+#endif // !defined(__aarch64__)
+
+ case __NR_execve:
+ case __NR_faccessat: // EPERM not a valid errno.
+ case __NR_fchmodat:
+ case __NR_fchownat: // Should be called chownat ?
+#if defined(__x86_64__) || defined(__aarch64__)
+ case __NR_newfstatat: // fstatat(). EPERM not a valid errno.
+#elif defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstatat64:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_lchown32:
+#endif
+ case __NR_linkat:
+ case __NR_lookup_dcookie: // ENOENT not a valid errno.
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_lstat64:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+ case __NR_memfd_create:
+#endif
+ case __NR_mkdirat:
+ case __NR_mknodat:
+#if defined(__i386__)
+ case __NR_oldlstat:
+ case __NR_oldstat:
+#endif
+ case __NR_openat:
+ case __NR_readlinkat:
+ case __NR_renameat:
+ case __NR_renameat2:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_stat64:
+#endif
+ case __NR_statfs: // EPERM not a valid errno.
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_statfs64:
+#endif
+ case __NR_symlinkat:
+ case __NR_truncate:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_truncate64:
+#endif
+ case __NR_unlinkat:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_utime:
+#endif
+ case __NR_utimensat: // New.
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedFileSystemAccessViaFd(int sysno) {
+ switch (sysno) {
+ case __NR_fstat:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstat64:
+#endif
+ return true;
+// TODO(jln): these should be denied gracefully as well (moved below).
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_fadvise64: // EPERM not a valid errno.
+#endif
+#if defined(__i386__)
+ case __NR_fadvise64_64:
+#endif
+#if defined(__arm__)
+ case __NR_arm_fadvise64_64:
+#endif
+ case __NR_fdatasync: // EPERM not a valid errno.
+ case __NR_flock: // EPERM not a valid errno.
+ case __NR_fstatfs: // Give information about the whole filesystem.
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstatfs64:
+#endif
+ case __NR_fsync: // EPERM not a valid errno.
+#if defined(__i386__)
+ case __NR_oldfstat:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_sync_file_range: // EPERM not a valid errno.
+#elif defined(__arm__)
+ case __NR_arm_sync_file_range: // EPERM not a valid errno.
+#endif
+ default:
+ return false;
+ }
+}
+
+// EPERM is a good errno for any of these.
+bool SyscallSets::IsDeniedFileSystemAccessViaFd(int sysno) {
+ switch (sysno) {
+ case __NR_fallocate:
+ case __NR_fchmod:
+ case __NR_fchown:
+ case __NR_ftruncate:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_fchown32:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_ftruncate64:
+#endif
+#if !defined(__aarch64__)
+ case __NR_getdents: // EPERM not a valid errno.
+#endif
+ case __NR_getdents64: // EPERM not a valid errno.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_readdir:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGetSimpleId(int sysno) {
+ switch (sysno) {
+ case __NR_capget:
+ case __NR_getegid:
+ case __NR_geteuid:
+ case __NR_getgid:
+ case __NR_getgroups:
+ case __NR_getpid:
+ case __NR_getppid:
+ case __NR_getresgid:
+ case __NR_getsid:
+ case __NR_gettid:
+ case __NR_getuid:
+ case __NR_getresuid:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_getegid32:
+ case __NR_geteuid32:
+ case __NR_getgid32:
+ case __NR_getgroups32:
+ case __NR_getresgid32:
+ case __NR_getresuid32:
+ case __NR_getuid32:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsProcessPrivilegeChange(int sysno) {
+ switch (sysno) {
+ case __NR_capset:
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_ioperm: // Intel privilege.
+ case __NR_iopl: // Intel privilege.
+#endif
+ case __NR_setfsgid:
+ case __NR_setfsuid:
+ case __NR_setgid:
+ case __NR_setgroups:
+ case __NR_setregid:
+ case __NR_setresgid:
+ case __NR_setresuid:
+ case __NR_setreuid:
+ case __NR_setuid:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_setfsgid32:
+ case __NR_setfsuid32:
+ case __NR_setgid32:
+ case __NR_setgroups32:
+ case __NR_setregid32:
+ case __NR_setresgid32:
+ case __NR_setresuid32:
+ case __NR_setreuid32:
+ case __NR_setuid32:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsProcessGroupOrSession(int sysno) {
+ switch (sysno) {
+ case __NR_setpgid:
+#if !defined(__aarch64__)
+ case __NR_getpgrp:
+#endif
+ case __NR_setsid:
+ case __NR_getpgid:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedSignalHandling(int sysno) {
+ switch (sysno) {
+ case __NR_rt_sigaction:
+ case __NR_rt_sigprocmask:
+ case __NR_rt_sigreturn:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sigaction:
+ case __NR_sigprocmask:
+ case __NR_sigreturn:
+#endif
+ return true;
+ case __NR_rt_sigpending:
+ case __NR_rt_sigqueueinfo:
+ case __NR_rt_sigsuspend:
+ case __NR_rt_sigtimedwait:
+ case __NR_rt_tgsigqueueinfo:
+ case __NR_sigaltstack:
+#if !defined(__aarch64__)
+ case __NR_signalfd:
+#endif
+ case __NR_signalfd4:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sigpending:
+ case __NR_sigsuspend:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_signal:
+ case __NR_sgetmask: // Obsolete.
+ case __NR_ssetmask:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedOperationOnFd(int sysno) {
+ switch (sysno) {
+ case __NR_close:
+ case __NR_dup:
+#if !defined(__aarch64__)
+ case __NR_dup2:
+#endif
+ case __NR_dup3:
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_shutdown:
+#endif
+ return true;
+ case __NR_fcntl:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fcntl64:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKernelInternalApi(int sysno) {
+ switch (sysno) {
+ case __NR_restart_syscall:
+#if defined(__arm__)
+ case __ARM_NR_cmpxchg:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+// This should be thought through in conjunction with IsFutex().
+bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) {
+ switch (sysno) {
+ case __NR_exit:
+ case __NR_exit_group:
+ case __NR_wait4:
+ case __NR_waitid:
+#if defined(__i386__)
+ case __NR_waitpid:
+#endif
+ return true;
+ case __NR_clone: // Should be parameter-restricted.
+ case __NR_setns: // Privileged.
+#if !defined(__aarch64__)
+ case __NR_fork:
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_get_thread_area:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_set_thread_area:
+#endif
+ case __NR_set_tid_address:
+ case __NR_unshare:
+#if !defined(__mips__) && !defined(__aarch64__)
+ case __NR_vfork:
+#endif
+ default:
+ return false;
+ }
+}
+
+// It's difficult to restrict those, but there is attack surface here.
+bool SyscallSets::IsAllowedFutex(int sysno) {
+ switch (sysno) {
+ case __NR_get_robust_list:
+ case __NR_set_robust_list:
+ case __NR_futex:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedEpoll(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_epoll_create:
+ case __NR_epoll_wait:
+#endif
+ case __NR_epoll_create1:
+ case __NR_epoll_ctl:
+ return true;
+ default:
+#if defined(__x86_64__)
+ case __NR_epoll_ctl_old:
+#endif
+ case __NR_epoll_pwait:
+#if defined(__x86_64__)
+ case __NR_epoll_wait_old:
+#endif
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGetOrModifySocket(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_pipe:
+#endif
+ case __NR_pipe2:
+ return true;
+ default:
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_socketpair: // We will want to inspect its argument.
+#endif
+ return false;
+ }
+}
+
+bool SyscallSets::IsDeniedGetOrModifySocket(int sysno) {
+ switch (sysno) {
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_accept:
+ case __NR_accept4:
+ case __NR_bind:
+ case __NR_connect:
+ case __NR_socket:
+ case __NR_listen:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+#if defined(__i386__) || defined(__mips__)
+// Big multiplexing system call for sockets.
+bool SyscallSets::IsSocketCall(int sysno) {
+ switch (sysno) {
+ case __NR_socketcall:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__)
+bool SyscallSets::IsNetworkSocketInformation(int sysno) {
+ switch (sysno) {
+ case __NR_getpeername:
+ case __NR_getsockname:
+ case __NR_getsockopt:
+ case __NR_setsockopt:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+bool SyscallSets::IsAllowedAddressSpaceAccess(int sysno) {
+ switch (sysno) {
+ case __NR_brk:
+ case __NR_mlock:
+ case __NR_munlock:
+ case __NR_munmap:
+ return true;
+ case __NR_madvise:
+ case __NR_mincore:
+ case __NR_mlockall:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_mmap:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_mmap2:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_modify_ldt:
+#endif
+ case __NR_mprotect:
+ case __NR_mremap:
+ case __NR_msync:
+ case __NR_munlockall:
+ case __NR_readahead:
+ case __NR_remap_file_pages:
+#if defined(__i386__)
+ case __NR_vm86:
+ case __NR_vm86old:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGeneralIo(int sysno) {
+ switch (sysno) {
+ case __NR_lseek:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR__llseek:
+#endif
+#if !defined(__aarch64__)
+ case __NR_poll:
+#endif
+ case __NR_ppoll:
+ case __NR_pselect6:
+ case __NR_read:
+ case __NR_readv:
+#if defined(__arm__) || defined(__mips__)
+ case __NR_recv:
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_recvfrom: // Could specify source.
+ case __NR_recvmsg: // Could specify source.
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_select:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR__newselect:
+#endif
+#if defined(__arm__)
+ case __NR_send:
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_sendmsg: // Could specify destination.
+ case __NR_sendto: // Could specify destination.
+#endif
+ case __NR_write:
+ case __NR_writev:
+ return true;
+ case __NR_ioctl: // Can be very powerful.
+ case __NR_pread64:
+ case __NR_preadv:
+ case __NR_pwrite64:
+ case __NR_pwritev:
+ case __NR_recvmmsg: // Could specify source.
+ case __NR_sendfile:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sendfile64:
+#endif
+ case __NR_sendmmsg: // Could specify destination.
+ case __NR_splice:
+ case __NR_tee:
+ case __NR_vmsplice:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsPrctl(int sysno) {
+ switch (sysno) {
+#if defined(__x86_64__)
+ case __NR_arch_prctl:
+#endif
+ case __NR_prctl:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsSeccomp(int sysno) {
+ switch (sysno) {
+ case __NR_seccomp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedBasicScheduler(int sysno) {
+ switch (sysno) {
+ case __NR_sched_yield:
+#if !defined(__aarch64__)
+ case __NR_pause:
+#endif
+ case __NR_nanosleep:
+ return true;
+ case __NR_getpriority:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_nice:
+#endif
+ case __NR_setpriority:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAdminOperation(int sysno) {
+ switch (sysno) {
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_bdflush:
+#endif
+ case __NR_kexec_load:
+ case __NR_reboot:
+ case __NR_setdomainname:
+ case __NR_sethostname:
+ case __NR_syslog:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKernelModule(int sysno) {
+ switch (sysno) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_create_module:
+ case __NR_get_kernel_syms: // Should ENOSYS.
+ case __NR_query_module:
+#endif
+ case __NR_delete_module:
+ case __NR_init_module:
+ case __NR_finit_module:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalFSViewChange(int sysno) {
+ switch (sysno) {
+ case __NR_pivot_root:
+ case __NR_chroot:
+ case __NR_sync:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsFsControl(int sysno) {
+ switch (sysno) {
+ case __NR_mount:
+ case __NR_nfsservctl:
+ case __NR_quotactl:
+ case __NR_swapoff:
+ case __NR_swapon:
+#if defined(__i386__) || defined(__mips__)
+ case __NR_umount:
+#endif
+ case __NR_umount2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsNuma(int sysno) {
+ switch (sysno) {
+ case __NR_get_mempolicy:
+ case __NR_getcpu:
+ case __NR_mbind:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_migrate_pages:
+#endif
+ case __NR_move_pages:
+ case __NR_set_mempolicy:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsMessageQueue(int sysno) {
+ switch (sysno) {
+ case __NR_mq_getsetattr:
+ case __NR_mq_notify:
+ case __NR_mq_open:
+ case __NR_mq_timedreceive:
+ case __NR_mq_timedsend:
+ case __NR_mq_unlink:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalProcessEnvironment(int sysno) {
+ switch (sysno) {
+ case __NR_acct: // Privileged.
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_getrlimit:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_ugetrlimit:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_ulimit:
+#endif
+ case __NR_getrusage:
+ case __NR_personality: // Can change its personality as well.
+ case __NR_prlimit64: // Like setrlimit / getrlimit.
+ case __NR_setrlimit:
+ case __NR_times:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsDebug(int sysno) {
+ switch (sysno) {
+ case __NR_ptrace:
+ case __NR_process_vm_readv:
+ case __NR_process_vm_writev:
+ case __NR_kcmp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalSystemStatus(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR__sysctl:
+ case __NR_sysfs:
+#endif
+ case __NR_sysinfo:
+ case __NR_uname:
+#if defined(__i386__)
+ case __NR_olduname:
+ case __NR_oldolduname:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsEventFd(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_eventfd:
+#endif
+ case __NR_eventfd2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Asynchronous I/O API.
+bool SyscallSets::IsAsyncIo(int sysno) {
+ switch (sysno) {
+ case __NR_io_cancel:
+ case __NR_io_destroy:
+ case __NR_io_getevents:
+ case __NR_io_setup:
+ case __NR_io_submit:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKeyManagement(int sysno) {
+ switch (sysno) {
+ case __NR_add_key:
+ case __NR_keyctl:
+ case __NR_request_key:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+bool SyscallSets::IsSystemVSemaphores(int sysno) {
+ switch (sysno) {
+ case __NR_semctl:
+ case __NR_semget:
+ case __NR_semop:
+ case __NR_semtimedop:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+// These give a lot of ambient authority and bypass the setuid sandbox.
+bool SyscallSets::IsSystemVSharedMemory(int sysno) {
+ switch (sysno) {
+ case __NR_shmat:
+ case __NR_shmctl:
+ case __NR_shmdt:
+ case __NR_shmget:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+bool SyscallSets::IsSystemVMessageQueue(int sysno) {
+ switch (sysno) {
+ case __NR_msgctl:
+ case __NR_msgget:
+ case __NR_msgrcv:
+ case __NR_msgsnd:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__i386__) || defined(__mips__)
+// Big system V multiplexing system call.
+bool SyscallSets::IsSystemVIpc(int sysno) {
+ switch (sysno) {
+ case __NR_ipc:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+bool SyscallSets::IsAnySystemV(int sysno) {
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ return IsSystemVMessageQueue(sysno) || IsSystemVSemaphores(sysno) ||
+ IsSystemVSharedMemory(sysno);
+#elif defined(__i386__) || defined(__mips__)
+ return IsSystemVIpc(sysno);
+#endif
+}
+
+bool SyscallSets::IsAdvancedScheduler(int sysno) {
+ switch (sysno) {
+ case __NR_ioprio_get: // IO scheduler.
+ case __NR_ioprio_set:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsInotify(int sysno) {
+ switch (sysno) {
+ case __NR_inotify_add_watch:
+#if !defined(__aarch64__)
+ case __NR_inotify_init:
+#endif
+ case __NR_inotify_init1:
+ case __NR_inotify_rm_watch:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsFaNotify(int sysno) {
+ switch (sysno) {
+ case __NR_fanotify_init:
+ case __NR_fanotify_mark:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsTimer(int sysno) {
+ switch (sysno) {
+ case __NR_getitimer:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_alarm:
+#endif
+ case __NR_setitimer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAdvancedTimer(int sysno) {
+ switch (sysno) {
+ case __NR_timer_create:
+ case __NR_timer_delete:
+ case __NR_timer_getoverrun:
+ case __NR_timer_gettime:
+ case __NR_timer_settime:
+ case __NR_timerfd_create:
+ case __NR_timerfd_gettime:
+ case __NR_timerfd_settime:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsExtendedAttributes(int sysno) {
+ switch (sysno) {
+ case __NR_fgetxattr:
+ case __NR_flistxattr:
+ case __NR_fremovexattr:
+ case __NR_fsetxattr:
+ case __NR_getxattr:
+ case __NR_lgetxattr:
+ case __NR_listxattr:
+ case __NR_llistxattr:
+ case __NR_lremovexattr:
+ case __NR_lsetxattr:
+ case __NR_removexattr:
+ case __NR_setxattr:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Various system calls that need to be researched.
+// TODO(jln): classify this better.
+bool SyscallSets::IsMisc(int sysno) {
+ switch (sysno) {
+#if !defined(__mips__)
+ case __NR_getrandom:
+#endif
+ case __NR_name_to_handle_at:
+ case __NR_open_by_handle_at:
+ case __NR_perf_event_open:
+ case __NR_syncfs:
+ case __NR_vhangup:
+// The system calls below are not implemented.
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_afs_syscall:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_break:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_getpmsg:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_gtty:
+ case __NR_idle:
+ case __NR_lock:
+ case __NR_mpx:
+ case __NR_prof:
+ case __NR_profil:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_putpmsg:
+#endif
+#if defined(__x86_64__)
+ case __NR_security:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_stty:
+#endif
+#if defined(__x86_64__)
+ case __NR_tuxcall:
+#endif
+#if !defined(__aarch64__)
+ case __NR_vserver:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if defined(__arm__)
+bool SyscallSets::IsArmPciConfig(int sysno) {
+ switch (sysno) {
+ case __NR_pciconfig_iobase:
+ case __NR_pciconfig_read:
+ case __NR_pciconfig_write:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsArmPrivate(int sysno) {
+ switch (sysno) {
+ case __ARM_NR_breakpoint:
+ case __ARM_NR_cacheflush:
+ case __ARM_NR_set_tls:
+ case __ARM_NR_usr26:
+ case __ARM_NR_usr32:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // defined(__arm__)
+
+#if defined(__mips__)
+bool SyscallSets::IsMipsPrivate(int sysno) {
+ switch (sysno) {
+ case __NR_cacheflush:
+ case __NR_cachectl:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsMipsMisc(int sysno) {
+ switch (sysno) {
+ case __NR_sysmips:
+ case __NR_unused150:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // defined(__mips__)
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
new file mode 100644
index 0000000..1dbd949
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "sandbox/sandbox_export.h"
+
+// These are helpers to build seccomp-bpf policies, i.e. policies for a
+// sandbox that reduces the Linux kernel's attack surface. Given their
+// nature, they don't have any clear semantics and are completely
+// "implementation-defined".
+
+namespace sandbox {
+
+class SANDBOX_EXPORT SyscallSets {
+ public:
+ static bool IsKill(int sysno);
+ static bool IsAllowedGettime(int sysno);
+ static bool IsCurrentDirectory(int sysno);
+ static bool IsUmask(int sysno);
+ // System calls that directly access the file system. They might acquire
+ // a new file descriptor or otherwise perform an operation directly
+ // via a path.
+ static bool IsFileSystem(int sysno);
+ static bool IsAllowedFileSystemAccessViaFd(int sysno);
+ static bool IsDeniedFileSystemAccessViaFd(int sysno);
+ static bool IsGetSimpleId(int sysno);
+ static bool IsProcessPrivilegeChange(int sysno);
+ static bool IsProcessGroupOrSession(int sysno);
+ static bool IsAllowedSignalHandling(int sysno);
+ static bool IsAllowedOperationOnFd(int sysno);
+ static bool IsKernelInternalApi(int sysno);
+ // This should be thought through in conjunction with IsFutex().
+ static bool IsAllowedProcessStartOrDeath(int sysno);
+ // It's difficult to restrict those, but there is attack surface here.
+ static bool IsAllowedFutex(int sysno);
+ static bool IsAllowedEpoll(int sysno);
+ static bool IsAllowedGetOrModifySocket(int sysno);
+ static bool IsDeniedGetOrModifySocket(int sysno);
+
+#if defined(__i386__) || defined(__mips__)
+ // Big multiplexing system call for sockets.
+ static bool IsSocketCall(int sysno);
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ static bool IsNetworkSocketInformation(int sysno);
+#endif
+
+ static bool IsAllowedAddressSpaceAccess(int sysno);
+ static bool IsAllowedGeneralIo(int sysno);
+ static bool IsPrctl(int sysno);
+ static bool IsSeccomp(int sysno);
+ static bool IsAllowedBasicScheduler(int sysno);
+ static bool IsAdminOperation(int sysno);
+ static bool IsKernelModule(int sysno);
+ static bool IsGlobalFSViewChange(int sysno);
+ static bool IsFsControl(int sysno);
+ static bool IsNuma(int sysno);
+ static bool IsMessageQueue(int sysno);
+ static bool IsGlobalProcessEnvironment(int sysno);
+ static bool IsDebug(int sysno);
+ static bool IsGlobalSystemStatus(int sysno);
+ static bool IsEventFd(int sysno);
+ // Asynchronous I/O API.
+ static bool IsAsyncIo(int sysno);
+ static bool IsKeyManagement(int sysno);
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ static bool IsSystemVSemaphores(int sysno);
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ // These give a lot of ambient authority and bypass the setuid sandbox.
+ static bool IsSystemVSharedMemory(int sysno);
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ static bool IsSystemVMessageQueue(int sysno);
+#endif
+
+#if defined(__i386__) || defined(__mips__)
+ // Big system V multiplexing system call.
+ static bool IsSystemVIpc(int sysno);
+#endif
+
+ static bool IsAnySystemV(int sysno);
+ static bool IsAdvancedScheduler(int sysno);
+ static bool IsInotify(int sysno);
+ static bool IsFaNotify(int sysno);
+ static bool IsTimer(int sysno);
+ static bool IsAdvancedTimer(int sysno);
+ static bool IsExtendedAttributes(int sysno);
+ static bool IsMisc(int sysno);
+#if defined(__arm__)
+ static bool IsArmPciConfig(int sysno);
+ static bool IsArmPrivate(int sysno);
+#endif // defined(__arm__)
+#if defined(__mips__)
+ static bool IsMipsPrivate(int sysno);
+ static bool IsMipsMisc(int sysno);
+#endif // defined(__mips__)
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallSets);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
diff --git a/sandbox/linux/seccomp-bpf/DEPS b/sandbox/linux/seccomp-bpf/DEPS
new file mode 100644
index 0000000..7fef15f
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+sandbox/linux/bpf_dsl",
+ "+sandbox/linux/services",
+]
diff --git a/sandbox/linux/seccomp-bpf/basicblock.cc b/sandbox/linux/seccomp-bpf/basicblock.cc
new file mode 100644
index 0000000..eb857f0
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/basicblock.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/basicblock.h"
+
+namespace sandbox {
+
+BasicBlock::BasicBlock() {}
+
+BasicBlock::~BasicBlock() {}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/basicblock.h b/sandbox/linux/seccomp-bpf/basicblock.h
new file mode 100644
index 0000000..d15a372
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/basicblock.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__
+
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/instruction.h"
+
+namespace sandbox {
+
+struct BasicBlock {
+ BasicBlock();
+ ~BasicBlock();
+
+ // Our implementation of the code generator uses a "Less" operator to
+ // identify common sequences of basic blocks. This would normally be
+ // really easy to do, but STL requires us to wrap the comparator into
+ // a class. We begrudgingly add some code here that provides this wrapping.
+ template <class T>
+ class Less {
+ public:
+ Less(const T& data,
+ int (*cmp)(const BasicBlock*, const BasicBlock*, const T& data))
+ : data_(data), cmp_(cmp) {}
+
+ bool operator()(const BasicBlock* a, const BasicBlock* b) const {
+ return cmp_(a, b, data_) < 0;
+ }
+
+ private:
+ const T& data_;
+ int (*cmp_)(const BasicBlock*, const BasicBlock*, const T&);
+ };
+
+ // Basic blocks are essentially nothing more than a set of instructions.
+ std::vector<Instruction*> instructions;
+
+ // In order to compute relative branch offsets we need to keep track of
+ // how far our block is away from the very last basic block. The "offset_"
+ // is measured in number of BPF instructions.
+ int offset;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__
diff --git a/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
new file mode 100644
index 0000000..edaa4bf
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+namespace sandbox {
+
+// This templated class allows building a BPFTesterDelegate from a
+// deprecated-style BPF policy (that is a SyscallEvaluator function pointer,
+// instead of a SandboxBPFPolicy class), specified in |policy_function| and a
+// function pointer to a test in |test_function|.
+// This allows both the policy and the test function to take a pointer to an
+// object of type "Aux" as a parameter. This is used to implement the BPF_TEST
+// macro and should generally not be used directly.
+template <class Policy, class Aux>
+class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
+ public:
+ typedef void (*TestFunction)(Aux*);
+
+ explicit BPFTesterCompatibilityDelegate(TestFunction test_function)
+ : aux_(), test_function_(test_function) {}
+
+ virtual ~BPFTesterCompatibilityDelegate() {}
+
+ virtual scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> GetSandboxBPFPolicy()
+ override {
+ // The current method is guaranteed to only run in the child process
+ // running the test. In this process, the current object is guaranteed
+ // to live forever. So it's ok to pass aux_pointer_for_policy_ to
+ // the policy, which could in turn pass it to the kernel via Trap().
+ return scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy>(new Policy(&aux_));
+ }
+
+ virtual void RunTestFunction() override {
+ // Run the actual test.
+ // The current object is guaranteed to live forever in the child process
+ // where this will run.
+ test_function_(&aux_);
+ }
+
+ private:
+ Aux aux_;
+ TestFunction test_function_;
+
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterCompatibilityDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h
new file mode 100644
index 0000000..879eb21
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+// BPF_TEST_C() is a special version of SANDBOX_TEST(). It runs a test function
+// in a sub-process, under a seccomp-bpf policy specified in
+// |bpf_policy_class_name| without failing on configurations that are allowed
+// to not support seccomp-bpf in their kernels.
+// This is the preferred format for new BPF tests. |bpf_policy_class_name| is a
+// class name (which will be default-constructed) that implements the
+// SandboxBPFDSLPolicy interface.
+// The test function's body can simply follow. Test functions should use
+// the BPF_ASSERT macros defined below, not GTEST's macros. The use of
+// CHECK* macros is supported but less robust.
+#define BPF_TEST_C(test_case_name, test_name, bpf_policy_class_name) \
+ BPF_DEATH_TEST_C( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_policy_class_name)
+
+// Identical to BPF_TEST_C but allows to specify the nature of death.
+#define BPF_DEATH_TEST_C( \
+ test_case_name, test_name, death, bpf_policy_class_name) \
+ void BPF_TEST_C_##test_name(); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterSimpleDelegate<bpf_policy_class_name>( \
+ BPF_TEST_C_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_C_##test_name()
+
+// This form of BPF_TEST is a little verbose and should be reserved for complex
+// tests where a lot of control is required.
+// |bpf_tester_delegate_class| must be a classname implementing the
+// BPFTesterDelegate interface.
+#define BPF_TEST_D(test_case_name, test_name, bpf_tester_delegate_class) \
+ BPF_DEATH_TEST_D( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_tester_delegate_class)
+
+// Identical to BPF_TEST_D but allows to specify the nature of death.
+#define BPF_DEATH_TEST_D( \
+ test_case_name, test_name, death, bpf_tester_delegate_class) \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new bpf_tester_delegate_class()); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ }
+
+// Assertions are handled exactly the same as with a normal SANDBOX_TEST()
+#define BPF_ASSERT SANDBOX_ASSERT
+#define BPF_ASSERT_EQ(x, y) BPF_ASSERT((x) == (y))
+#define BPF_ASSERT_NE(x, y) BPF_ASSERT((x) != (y))
+#define BPF_ASSERT_LT(x, y) BPF_ASSERT((x) < (y))
+#define BPF_ASSERT_GT(x, y) BPF_ASSERT((x) > (y))
+#define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y))
+#define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y))
+
+// This form of BPF_TEST is now discouraged (but still allowed) in favor of
+// BPF_TEST_D and BPF_TEST_C.
+// The |policy| parameter should be a SandboxBPFDSLPolicy subclass.
+// BPF_TEST() takes a C++ data type as an fourth parameter. A variable
+// of this type will be allocated and a pointer to it will be
+// available within the test function as "BPF_AUX". The pointer will
+// also be passed as an argument to the policy's constructor. Policies
+// would typically use it as an argument to SandboxBPF::Trap(), if
+// they want to communicate data between the BPF_TEST() and a Trap()
+// function. The life-time of this object is the same as the life-time
+// of the process running under the seccomp-bpf policy.
+// |aux| must not be void.
+#define BPF_TEST(test_case_name, test_name, policy, aux) \
+ BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux)
+
+// A BPF_DEATH_TEST is just the same as a BPF_TEST, but it assumes that the
+// test will fail with a particular known error condition. Use the DEATH_XXX()
+// macros from unit_tests.h to specify the expected error condition.
+#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux) \
+ void BPF_TEST_##test_name(aux* BPF_AUX); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterCompatibilityDelegate<policy, aux>( \
+ BPF_TEST_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_##test_name(aux* BPF_AUX)
+
+// This class takes a simple function pointer as a constructor parameter and a
+// class name as a template parameter to implement the BPFTesterDelegate
+// interface which can be used to build BPF unittests with
+// the SandboxBPFTestRunner class.
+template <class PolicyClass>
+class BPFTesterSimpleDelegate : public BPFTesterDelegate {
+ public:
+ explicit BPFTesterSimpleDelegate(void (*test_function)(void))
+ : test_function_(test_function) {}
+ virtual ~BPFTesterSimpleDelegate() {}
+
+ virtual scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> GetSandboxBPFPolicy()
+ override {
+ return scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy>(new PolicyClass());
+ }
+ virtual void RunTestFunction() override {
+ DCHECK(test_function_);
+ test_function_();
+ }
+
+ private:
+ void (*test_function_)(void);
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterSimpleDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
new file mode 100644
index 0000000..24b82fa
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::SandboxBPFDSLPolicy;
+
+namespace sandbox {
+
+namespace {
+
+class FourtyTwo {
+ public:
+ static const int kMagicValue = 42;
+ FourtyTwo() : value_(kMagicValue) {}
+ int value() { return value_; }
+
+ private:
+ int value_;
+ DISALLOW_COPY_AND_ASSIGN(FourtyTwo);
+};
+
+class EmptyClassTakingPolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit EmptyClassTakingPolicy(FourtyTwo* fourty_two) {
+ BPF_ASSERT(fourty_two);
+ BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
+ }
+ virtual ~EmptyClassTakingPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ return Allow();
+ }
+};
+
+BPF_TEST(BPFTest,
+ BPFAUXPointsToClass,
+ EmptyClassTakingPolicy,
+ FourtyTwo /* *BPF_AUX */) {
+ // BPF_AUX should point to an instance of FourtyTwo.
+ BPF_ASSERT(BPF_AUX);
+ BPF_ASSERT(FourtyTwo::kMagicValue == BPF_AUX->value());
+}
+
+void DummyTestFunction(FourtyTwo *fourty_two) {
+}
+
+TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) {
+ // Don't do anything, simply gives dynamic tools an opportunity to detect
+ // leaks.
+ {
+ BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>
+ simple_delegate(DummyTestFunction);
+ }
+ {
+ // Test polymorphism.
+ scoped_ptr<BPFTesterDelegate> simple_delegate(
+ new BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>(
+ DummyTestFunction));
+ }
+}
+
+class EnosysPtracePolicy : public SandboxBPFDSLPolicy {
+ public:
+ EnosysPtracePolicy() {
+ my_pid_ = syscall(__NR_getpid);
+ }
+ virtual ~EnosysPtracePolicy() {
+ // Policies should be able to bind with the process on which they are
+ // created. They should never be created in a parent process.
+ BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid));
+ }
+
+ virtual ResultExpr EvaluateSyscall(int system_call_number) const override {
+ CHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
+ if (system_call_number == __NR_ptrace) {
+ // The EvaluateSyscall function should run in the process that created
+ // the current object.
+ BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid));
+ return Error(ENOSYS);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ pid_t my_pid_;
+ DISALLOW_COPY_AND_ASSIGN(EnosysPtracePolicy);
+};
+
+class BasicBPFTesterDelegate : public BPFTesterDelegate {
+ public:
+ BasicBPFTesterDelegate() {}
+ virtual ~BasicBPFTesterDelegate() {}
+
+ virtual scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> GetSandboxBPFPolicy()
+ override {
+ return scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy>(new EnosysPtracePolicy());
+ }
+ virtual void RunTestFunction() override {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicBPFTesterDelegate);
+};
+
+// This is the most powerful and complex way to create a BPF test, but it
+// requires a full class definition (BasicBPFTesterDelegate).
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, BasicBPFTesterDelegate);
+
+// This is the simplest form of BPF tests.
+BPF_TEST_C(BPFTest, BPFTestWithInlineTest, EnosysPtracePolicy) {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+}
+
+const char kHelloMessage[] = "Hello";
+
+BPF_DEATH_TEST_C(BPFTest,
+ BPFDeathTestWithInlineTest,
+ DEATH_MESSAGE(kHelloMessage),
+ EnosysPtracePolicy) {
+ LOG(ERROR) << kHelloMessage;
+ _exit(1);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/codegen.cc b/sandbox/linux/seccomp-bpf/codegen.cc
new file mode 100644
index 0000000..8169840
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/codegen.cc
@@ -0,0 +1,698 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+
+#include <stdio.h>
+
+#include <set>
+
+#include "base/logging.h"
+#include "sandbox/linux/seccomp-bpf/basicblock.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/instruction.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+namespace sandbox {
+
+CodeGen::CodeGen() : compiled_(false) {}
+
+CodeGen::~CodeGen() {
+ for (Instructions::iterator iter = instructions_.begin();
+ iter != instructions_.end();
+ ++iter) {
+ delete *iter;
+ }
+ for (BasicBlocks::iterator iter = basic_blocks_.begin();
+ iter != basic_blocks_.end();
+ ++iter) {
+ delete *iter;
+ }
+}
+
+void CodeGen::PrintProgram(const Program& program) {
+ for (Program::const_iterator iter = program.begin(); iter != program.end();
+ ++iter) {
+ int ip = (int)(iter - program.begin());
+ fprintf(stderr, "%3d) ", ip);
+ switch (BPF_CLASS(iter->code)) {
+ case BPF_LD:
+ if (iter->code == BPF_LD + BPF_W + BPF_ABS) {
+ fprintf(stderr, "LOAD %d // ", (int)iter->k);
+ if (iter->k == offsetof(struct arch_seccomp_data, nr)) {
+ fprintf(stderr, "System call number\n");
+ } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) {
+ fprintf(stderr, "Architecture\n");
+ } else if (iter->k ==
+ offsetof(struct arch_seccomp_data, instruction_pointer)) {
+ fprintf(stderr, "Instruction pointer (LSB)\n");
+ } else if (iter->k ==
+ offsetof(struct arch_seccomp_data, instruction_pointer) +
+ 4) {
+ fprintf(stderr, "Instruction pointer (MSB)\n");
+ } else if (iter->k >= offsetof(struct arch_seccomp_data, args) &&
+ iter->k < offsetof(struct arch_seccomp_data, args) + 48 &&
+ (iter->k - offsetof(struct arch_seccomp_data, args)) % 4 ==
+ 0) {
+ fprintf(
+ stderr,
+ "Argument %d (%cSB)\n",
+ (int)(iter->k - offsetof(struct arch_seccomp_data, args)) / 8,
+ (iter->k - offsetof(struct arch_seccomp_data, args)) % 8 ? 'M'
+ : 'L');
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ } else {
+ fprintf(stderr, "LOAD ???\n");
+ }
+ break;
+ case BPF_JMP:
+ if (BPF_OP(iter->code) == BPF_JA) {
+ fprintf(stderr, "JMP %d\n", ip + iter->k + 1);
+ } else {
+ fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n",
+ BPF_OP(iter->code) == BPF_JSET ? "&" :
+ BPF_OP(iter->code) == BPF_JEQ ? "==" :
+ BPF_OP(iter->code) == BPF_JGE ? ">=" :
+ BPF_OP(iter->code) == BPF_JGT ? ">" : "???",
+ (int)iter->k,
+ ip + iter->jt + 1, ip + iter->jf + 1);
+ }
+ break;
+ case BPF_RET:
+ fprintf(stderr, "RET 0x%x // ", iter->k);
+ if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
+ fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA);
+ } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) {
+ fprintf(stderr, "Trace #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if (iter->k == SECCOMP_RET_ALLOW) {
+ fprintf(stderr, "Allowed\n");
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ break;
+ case BPF_ALU:
+ fprintf(stderr, BPF_OP(iter->code) == BPF_NEG
+ ? "A := -A\n" : "A := A %s 0x%x\n",
+ BPF_OP(iter->code) == BPF_ADD ? "+" :
+ BPF_OP(iter->code) == BPF_SUB ? "-" :
+ BPF_OP(iter->code) == BPF_MUL ? "*" :
+ BPF_OP(iter->code) == BPF_DIV ? "/" :
+ BPF_OP(iter->code) == BPF_MOD ? "%" :
+ BPF_OP(iter->code) == BPF_OR ? "|" :
+ BPF_OP(iter->code) == BPF_XOR ? "^" :
+ BPF_OP(iter->code) == BPF_AND ? "&" :
+ BPF_OP(iter->code) == BPF_LSH ? "<<" :
+ BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
+ (int)iter->k);
+ break;
+ default:
+ fprintf(stderr, "???\n");
+ break;
+ }
+ }
+ return;
+}
+
+Instruction* CodeGen::MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* next) {
+ // We can handle non-jumping instructions and "always" jumps. Both of
+ // them are followed by exactly one "next" instruction.
+ // We allow callers to defer specifying "next", but then they must call
+ // "joinInstructions" later.
+ if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
+ SANDBOX_DIE(
+ "Must provide both \"true\" and \"false\" branch "
+ "for a BPF_JMP");
+ }
+ if (next && BPF_CLASS(code) == BPF_RET) {
+ SANDBOX_DIE("Cannot append instructions after a return statement");
+ }
+ if (BPF_CLASS(code) == BPF_JMP) {
+ // "Always" jumps use the "true" branch target, only.
+ Instruction* insn = new Instruction(code, 0, next, NULL);
+ instructions_.push_back(insn);
+ return insn;
+ } else {
+ // Non-jumping instructions do not use any of the branch targets.
+ Instruction* insn = new Instruction(code, k, next);
+ instructions_.push_back(insn);
+ return insn;
+ }
+}
+
+Instruction* CodeGen::MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* jt,
+ Instruction* jf) {
+ // We can handle all conditional jumps. They are followed by both a
+ // "true" and a "false" branch.
+ if (BPF_CLASS(code) != BPF_JMP || BPF_OP(code) == BPF_JA) {
+ SANDBOX_DIE("Expected a BPF_JMP instruction");
+ }
+ if (!jt || !jf) {
+ SANDBOX_DIE("Branches must jump to a valid instruction");
+ }
+ Instruction* insn = new Instruction(code, k, jt, jf);
+ instructions_.push_back(insn);
+ return insn;
+}
+
+void CodeGen::FindBranchTargets(const Instruction& instructions,
+ BranchTargets* branch_targets) {
+ // Follow all possible paths through the "instructions" graph and compute
+ // a list of branch targets. This will later be needed to compute the
+ // boundaries of basic blocks.
+ // We maintain a set of all instructions that we have previously seen. This
+ // set ultimately converges on all instructions in the program.
+ std::set<const Instruction*> seen_instructions;
+ Instructions stack;
+ for (const Instruction* insn = &instructions; insn;) {
+ seen_instructions.insert(insn);
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // Found a jump. Increase count of incoming edges for each of the jump
+ // targets.
+ ++(*branch_targets)[insn->jt_ptr];
+ if (BPF_OP(insn->code) != BPF_JA) {
+ ++(*branch_targets)[insn->jf_ptr];
+ stack.push_back(const_cast<Instruction*>(insn));
+ }
+ // Start a recursive decent for depth-first traversal.
+ if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
+ // We haven't seen the "true" branch yet. Traverse it now. We have
+ // already remembered the "false" branch on the stack and will
+ // traverse it later.
+ insn = insn->jt_ptr;
+ continue;
+ } else {
+ // Now try traversing the "false" branch.
+ insn = NULL;
+ }
+ } else {
+ // This is a non-jump instruction, just continue to the next instruction
+ // (if any). It's OK if "insn" becomes NULL when reaching a return
+ // instruction.
+ if (!insn->next != (BPF_CLASS(insn->code) == BPF_RET)) {
+ SANDBOX_DIE(
+ "Internal compiler error; return instruction must be at "
+ "the end of the BPF program");
+ }
+ if (seen_instructions.find(insn->next) == seen_instructions.end()) {
+ insn = insn->next;
+ } else {
+ // We have seen this instruction before. That could happen if it is
+ // a branch target. No need to continue processing.
+ insn = NULL;
+ }
+ }
+ while (!insn && !stack.empty()) {
+ // We are done processing all the way to a leaf node, backtrack up the
+ // stack to any branches that we haven't processed yet. By definition,
+ // this has to be a "false" branch, as we always process the "true"
+ // branches right away.
+ insn = stack.back();
+ stack.pop_back();
+ if (seen_instructions.find(insn->jf_ptr) == seen_instructions.end()) {
+ // We haven't seen the "false" branch yet. So, that's where we'll
+ // go now.
+ insn = insn->jf_ptr;
+ } else {
+ // We have seen both the "true" and the "false" branch, continue
+ // up the stack.
+ if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
+ SANDBOX_DIE(
+ "Internal compiler error; cannot find all "
+ "branch targets");
+ }
+ insn = NULL;
+ }
+ }
+ }
+ return;
+}
+
+BasicBlock* CodeGen::MakeBasicBlock(Instruction* head, Instruction* tail) {
+ // Iterate over all the instructions between "head" and "tail" and
+ // insert them into a new basic block.
+ BasicBlock* bb = new BasicBlock;
+ for (;; head = head->next) {
+ bb->instructions.push_back(head);
+ if (head == tail) {
+ break;
+ }
+ if (BPF_CLASS(head->code) == BPF_JMP) {
+ SANDBOX_DIE("Found a jump inside of a basic block");
+ }
+ }
+ basic_blocks_.push_back(bb);
+ return bb;
+}
+
+void CodeGen::AddBasicBlock(Instruction* head,
+ Instruction* tail,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks* basic_blocks,
+ BasicBlock** firstBlock) {
+ // Add a new basic block to "basic_blocks". Also set "firstBlock", if it
+ // has not been set before.
+ BranchTargets::const_iterator iter = branch_targets.find(head);
+ if ((iter == branch_targets.end()) != !*firstBlock ||
+ !*firstBlock != basic_blocks->empty()) {
+ SANDBOX_DIE(
+ "Only the very first basic block should have no "
+ "incoming jumps");
+ }
+ BasicBlock* bb = MakeBasicBlock(head, tail);
+ if (!*firstBlock) {
+ *firstBlock = bb;
+ }
+ (*basic_blocks)[head] = bb;
+ return;
+}
+
+BasicBlock* CodeGen::CutGraphIntoBasicBlocks(
+ Instruction* instructions,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks* basic_blocks) {
+ // Textbook implementation of a basic block generator. All basic blocks
+ // start with a branch target and end with either a return statement or
+ // a jump (or are followed by an instruction that forms the beginning of a
+ // new block). Both conditional and "always" jumps are supported.
+ BasicBlock* first_block = NULL;
+ std::set<const Instruction*> seen_instructions;
+ Instructions stack;
+ Instruction* tail = NULL;
+ Instruction* head = instructions;
+ for (Instruction* insn = head; insn;) {
+ if (seen_instructions.find(insn) != seen_instructions.end()) {
+ // We somehow went in a circle. This should never be possible. Not even
+ // cyclic graphs are supposed to confuse us this much.
+ SANDBOX_DIE("Internal compiler error; cannot compute basic blocks");
+ }
+ seen_instructions.insert(insn);
+ if (tail && branch_targets.find(insn) != branch_targets.end()) {
+ // We reached a branch target. Start a new basic block (this means,
+ // flushing the previous basic block first).
+ AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block);
+ head = insn;
+ }
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // We reached a jump instruction, this completes our current basic
+ // block. Flush it and continue by traversing both the true and the
+ // false branch of the jump. We need to maintain a stack to do so.
+ AddBasicBlock(head, insn, branch_targets, basic_blocks, &first_block);
+ if (BPF_OP(insn->code) != BPF_JA) {
+ stack.push_back(insn->jf_ptr);
+ }
+ insn = insn->jt_ptr;
+
+ // If we are jumping to an instruction that we have previously
+ // processed, we are done with this branch. Continue by backtracking
+ // up the stack.
+ while (seen_instructions.find(insn) != seen_instructions.end()) {
+ backtracking:
+ if (stack.empty()) {
+ // We successfully traversed all reachable instructions.
+ return first_block;
+ } else {
+ // Going up the stack.
+ insn = stack.back();
+ stack.pop_back();
+ }
+ }
+ // Starting a new basic block.
+ tail = NULL;
+ head = insn;
+ } else {
+ // We found a non-jumping instruction, append it to current basic
+ // block.
+ tail = insn;
+ insn = insn->next;
+ if (!insn) {
+ // We reached a return statement, flush the current basic block and
+ // backtrack up the stack.
+ AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block);
+ goto backtracking;
+ }
+ }
+ }
+ return first_block;
+}
+
+// We define a comparator that inspects the sequence of instructions in our
+// basic block and any blocks referenced by this block. This function can be
+// used in a "less" comparator for the purpose of storing pointers to basic
+// blocks in STL containers; this gives an easy option to use STL to find
+// shared tail sequences of basic blocks.
+static int PointerCompare(const BasicBlock* block1,
+ const BasicBlock* block2,
+ const TargetsToBlocks& blocks) {
+ // Return <0, 0, or >0 depending on the ordering of "block1" and "block2".
+ // If we are looking at the exact same block, this is trivial and we don't
+ // need to do a full comparison.
+ if (block1 == block2) {
+ return 0;
+ }
+
+ // We compare the sequence of instructions in both basic blocks.
+ const Instructions& insns1 = block1->instructions;
+ const Instructions& insns2 = block2->instructions;
+ // Basic blocks should never be empty.
+ CHECK(!insns1.empty());
+ CHECK(!insns2.empty());
+
+ Instructions::const_iterator iter1 = insns1.begin();
+ Instructions::const_iterator iter2 = insns2.begin();
+ for (;; ++iter1, ++iter2) {
+ // If we have reached the end of the sequence of instructions in one or
+ // both basic blocks, we know the relative ordering between the two blocks
+ // and can return.
+ if (iter1 == insns1.end() || iter2 == insns2.end()) {
+ if (iter1 != insns1.end()) {
+ return 1;
+ }
+ if (iter2 != insns2.end()) {
+ return -1;
+ }
+
+ // If the two blocks are the same length (and have elementwise-equal code
+ // and k fields) and their last instructions are neither a JMP nor a RET
+ // (which is the only way we can reach this point), then we must compare
+ // their successors.
+ Instruction* const insns1_last = insns1.back();
+ Instruction* const insns2_last = insns2.back();
+ CHECK(BPF_CLASS(insns1_last->code) != BPF_JMP &&
+ BPF_CLASS(insns1_last->code) != BPF_RET);
+
+ // Non jumping instructions will always have a valid next instruction.
+ CHECK(insns1_last->next);
+ CHECK(insns2_last->next);
+ return PointerCompare(blocks.find(insns1_last->next)->second,
+ blocks.find(insns2_last->next)->second,
+ blocks);
+ }
+
+ // Compare the individual fields for both instructions.
+ const Instruction& insn1 = **iter1;
+ const Instruction& insn2 = **iter2;
+ if (insn1.code != insn2.code) {
+ return insn1.code - insn2.code;
+ }
+ if (insn1.k != insn2.k) {
+ return insn1.k - insn2.k;
+ }
+
+ // Sanity check: If we're looking at a JMP or RET instruction, by definition
+ // it should be the last instruction of the basic block.
+ if (BPF_CLASS(insn1.code) == BPF_JMP || BPF_CLASS(insn1.code) == BPF_RET) {
+ CHECK_EQ(insns1.back(), &insn1);
+ CHECK_EQ(insns2.back(), &insn2);
+ }
+
+ // RET instructions terminate execution, and only JMP instructions use the
+ // jt_ptr and jf_ptr fields. Anything else can continue to the next
+ // instruction in the basic block.
+ if (BPF_CLASS(insn1.code) == BPF_RET) {
+ return 0;
+ } else if (BPF_CLASS(insn1.code) != BPF_JMP) {
+ continue;
+ }
+
+ // Recursively compare the "true" and "false" branches.
+ // A well-formed BPF program can't have any cycles, so we know
+ // that our recursive algorithm will ultimately terminate.
+ // In the unlikely event that the programmer made a mistake and
+ // went out of the way to give us a cyclic program, we will crash
+ // with a stack overflow. We are OK with that.
+ if (BPF_OP(insn1.code) != BPF_JA) {
+ int c = PointerCompare(blocks.find(insn1.jf_ptr)->second,
+ blocks.find(insn2.jf_ptr)->second,
+ blocks);
+ if (c != 0) {
+ return c;
+ }
+ }
+ return PointerCompare(blocks.find(insn1.jt_ptr)->second,
+ blocks.find(insn2.jt_ptr)->second,
+ blocks);
+ }
+}
+
+void CodeGen::MergeTails(TargetsToBlocks* blocks) {
+ // We enter all of our basic blocks into a set using the BasicBlock::Less()
+ // comparator. This naturally results in blocks with identical tails of
+ // instructions to map to the same entry in the set. Whenever we discover
+ // that a particular chain of instructions is already in the set, we merge
+ // the basic blocks and update the pointer in the "blocks" map.
+ // Returns the number of unique basic blocks.
+ // N.B. We don't merge instructions on a granularity that is finer than
+ // a basic block. In practice, this is sufficiently rare that we don't
+ // incur a big cost.
+ // Similarly, we currently don't merge anything other than tails. In
+ // the future, we might decide to revisit this decision and attempt to
+ // merge arbitrary sub-sequences of instructions.
+ BasicBlock::Less<TargetsToBlocks> less(*blocks, PointerCompare);
+ typedef std::set<BasicBlock*, BasicBlock::Less<TargetsToBlocks> > Set;
+ Set seen_basic_blocks(less);
+ for (TargetsToBlocks::iterator iter = blocks->begin(); iter != blocks->end();
+ ++iter) {
+ BasicBlock* bb = iter->second;
+ Set::const_iterator entry = seen_basic_blocks.find(bb);
+ if (entry == seen_basic_blocks.end()) {
+ // This is the first time we see this particular sequence of
+ // instructions. Enter the basic block into the set of known
+ // basic blocks.
+ seen_basic_blocks.insert(bb);
+ } else {
+ // We have previously seen another basic block that defines the same
+ // sequence of instructions. Merge the two blocks and update the
+ // pointer in the "blocks" map.
+ iter->second = *entry;
+ }
+ }
+}
+
+void CodeGen::ComputeIncomingBranches(BasicBlock* block,
+ const TargetsToBlocks& targets_to_blocks,
+ IncomingBranches* incoming_branches) {
+ // We increment the number of incoming branches each time we encounter a
+ // basic block. But we only traverse recursively the very first time we
+ // encounter a new block. This is necessary to make topological sorting
+ // work correctly.
+ if (++(*incoming_branches)[block] == 1) {
+ Instruction* last_insn = block->instructions.back();
+ if (BPF_CLASS(last_insn->code) == BPF_JMP) {
+ ComputeIncomingBranches(targets_to_blocks.find(last_insn->jt_ptr)->second,
+ targets_to_blocks,
+ incoming_branches);
+ if (BPF_OP(last_insn->code) != BPF_JA) {
+ ComputeIncomingBranches(
+ targets_to_blocks.find(last_insn->jf_ptr)->second,
+ targets_to_blocks,
+ incoming_branches);
+ }
+ } else if (BPF_CLASS(last_insn->code) != BPF_RET) {
+ ComputeIncomingBranches(targets_to_blocks.find(last_insn->next)->second,
+ targets_to_blocks,
+ incoming_branches);
+ }
+ }
+}
+
+void CodeGen::TopoSortBasicBlocks(BasicBlock* first_block,
+ const TargetsToBlocks& blocks,
+ BasicBlocks* basic_blocks) {
+ // Textbook implementation of a toposort. We keep looking for basic blocks
+ // that don't have any incoming branches (initially, this is just the
+ // "first_block") and add them to the topologically sorted list of
+ // "basic_blocks". As we do so, we remove outgoing branches. This potentially
+ // ends up making our descendants eligible for the sorted list. The
+ // sorting algorithm terminates when there are no more basic blocks that have
+ // no incoming branches. If we didn't move all blocks from the set of
+ // "unordered_blocks" to the sorted list of "basic_blocks", there must have
+ // been a cyclic dependency. This should never happen in a BPF program, as
+ // well-formed BPF programs only ever have forward branches.
+ IncomingBranches unordered_blocks;
+ ComputeIncomingBranches(first_block, blocks, &unordered_blocks);
+
+ std::set<BasicBlock*> heads;
+ for (;;) {
+ // Move block from "unordered_blocks" to "basic_blocks".
+ basic_blocks->push_back(first_block);
+
+ // Inspect last instruction in the basic block. This is typically either a
+ // jump or a return statement. But it could also be a "normal" instruction
+ // that is followed by a jump target.
+ Instruction* last_insn = first_block->instructions.back();
+ if (BPF_CLASS(last_insn->code) == BPF_JMP) {
+ // Remove outgoing branches. This might end up moving our descendants
+ // into set of "head" nodes that no longer have any incoming branches.
+ TargetsToBlocks::const_iterator iter;
+ if (BPF_OP(last_insn->code) != BPF_JA) {
+ iter = blocks.find(last_insn->jf_ptr);
+ if (!--unordered_blocks[iter->second]) {
+ heads.insert(iter->second);
+ }
+ }
+ iter = blocks.find(last_insn->jt_ptr);
+ if (!--unordered_blocks[iter->second]) {
+ first_block = iter->second;
+ continue;
+ }
+ } else if (BPF_CLASS(last_insn->code) != BPF_RET) {
+ // We encountered an instruction that doesn't change code flow. Try to
+ // pick the next "first_block" from "last_insn->next", if possible.
+ TargetsToBlocks::const_iterator iter;
+ iter = blocks.find(last_insn->next);
+ if (!--unordered_blocks[iter->second]) {
+ first_block = iter->second;
+ continue;
+ } else {
+ // Our basic block is supposed to be followed by "last_insn->next",
+ // but dependencies prevent this from happening. Insert a BPF_JA
+ // instruction to correct the code flow.
+ Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, last_insn->next);
+ first_block->instructions.push_back(ja);
+ last_insn->next = ja;
+ }
+ }
+ if (heads.empty()) {
+ if (unordered_blocks.size() != basic_blocks->size()) {
+ SANDBOX_DIE("Internal compiler error; cyclic graph detected");
+ }
+ return;
+ }
+ // Proceed by picking an arbitrary node from the set of basic blocks that
+ // do not have any incoming branches.
+ first_block = *heads.begin();
+ heads.erase(heads.begin());
+ }
+}
+
+void CodeGen::ComputeRelativeJumps(BasicBlocks* basic_blocks,
+ const TargetsToBlocks& targets_to_blocks) {
+ // While we previously used pointers in jt_ptr and jf_ptr to link jump
+ // instructions to their targets, we now convert these jumps to relative
+ // jumps that are suitable for loading the BPF program into the kernel.
+ int offset = 0;
+
+ // Since we just completed a toposort, all jump targets are guaranteed to
+ // go forward. This means, iterating over the basic blocks in reverse makes
+ // it trivial to compute the correct offsets.
+ BasicBlock* bb = NULL;
+ BasicBlock* last_bb = NULL;
+ for (BasicBlocks::reverse_iterator iter = basic_blocks->rbegin();
+ iter != basic_blocks->rend();
+ ++iter) {
+ last_bb = bb;
+ bb = *iter;
+ Instruction* insn = bb->instructions.back();
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // Basic block ended in a jump instruction. We can now compute the
+ // appropriate offsets.
+ if (BPF_OP(insn->code) == BPF_JA) {
+ // "Always" jumps use the 32bit "k" field for the offset, instead
+ // of the 8bit "jt" and "jf" fields.
+ int jmp = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
+ insn->k = jmp;
+ insn->jt = insn->jf = 0;
+ } else {
+ // The offset computations for conditional jumps are just the same
+ // as for "always" jumps.
+ int jt = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
+ int jf = offset - targets_to_blocks.find(insn->jf_ptr)->second->offset;
+
+ // There is an added complication, because conditional relative jumps
+ // can only jump at most 255 instructions forward. If we have to jump
+ // further, insert an extra "always" jump.
+ Instructions::size_type jmp = bb->instructions.size();
+ if (jt > 255 || (jt == 255 && jf > 255)) {
+ Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jt_ptr);
+ bb->instructions.push_back(ja);
+ ja->k = jt;
+ ja->jt = ja->jf = 0;
+
+ // The newly inserted "always" jump, of course, requires us to adjust
+ // the jump targets in the original conditional jump.
+ jt = 0;
+ ++jf;
+ }
+ if (jf > 255) {
+ Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jf_ptr);
+ bb->instructions.insert(bb->instructions.begin() + jmp, ja);
+ ja->k = jf;
+ ja->jt = ja->jf = 0;
+
+ // Again, we have to adjust the jump targets in the original
+ // conditional jump.
+ ++jt;
+ jf = 0;
+ }
+
+ // Now we can finally set the relative jump targets in the conditional
+ // jump instruction. Afterwards, we must no longer access the jt_ptr
+ // and jf_ptr fields.
+ insn->jt = jt;
+ insn->jf = jf;
+ }
+ } else if (BPF_CLASS(insn->code) != BPF_RET &&
+ targets_to_blocks.find(insn->next)->second != last_bb) {
+ SANDBOX_DIE("Internal compiler error; invalid basic block encountered");
+ }
+
+ // Proceed to next basic block.
+ offset += bb->instructions.size();
+ bb->offset = offset;
+ }
+ return;
+}
+
+void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks,
+ Program* program) {
+ // Our basic blocks have been sorted and relative jump offsets have been
+ // computed. The last remaining step is for all the instructions in our
+ // basic blocks to be concatenated into a BPF program.
+ program->clear();
+ for (BasicBlocks::const_iterator bb_iter = basic_blocks.begin();
+ bb_iter != basic_blocks.end();
+ ++bb_iter) {
+ const BasicBlock& bb = **bb_iter;
+ for (Instructions::const_iterator insn_iter = bb.instructions.begin();
+ insn_iter != bb.instructions.end();
+ ++insn_iter) {
+ const Instruction& insn = **insn_iter;
+ program->push_back(
+ (struct sock_filter) {insn.code, insn.jt, insn.jf, insn.k});
+ }
+ }
+ return;
+}
+
+void CodeGen::Compile(Instruction* instructions, Program* program) {
+ if (compiled_) {
+ SANDBOX_DIE(
+ "Cannot call Compile() multiple times. Create a new code "
+ "generator instead");
+ }
+ compiled_ = true;
+
+ BranchTargets branch_targets;
+ FindBranchTargets(*instructions, &branch_targets);
+ TargetsToBlocks all_blocks;
+ BasicBlock* first_block =
+ CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks);
+ MergeTails(&all_blocks);
+ BasicBlocks basic_blocks;
+ TopoSortBasicBlocks(first_block, all_blocks, &basic_blocks);
+ ComputeRelativeJumps(&basic_blocks, all_blocks);
+ ConcatenateBasicBlocks(basic_blocks, program);
+ return;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/codegen.h b/sandbox/linux/seccomp-bpf/codegen.h
new file mode 100644
index 0000000..817b17c
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/codegen.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
+
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "sandbox/sandbox_export.h"
+
+struct sock_filter;
+
+namespace sandbox {
+struct BasicBlock;
+struct Instruction;
+
+typedef std::vector<Instruction*> Instructions;
+typedef std::vector<BasicBlock*> BasicBlocks;
+typedef std::map<const Instruction*, int> BranchTargets;
+typedef std::map<const Instruction*, BasicBlock*> TargetsToBlocks;
+typedef std::map<const BasicBlock*, int> IncomingBranches;
+
+// The code generator instantiates a basic compiler that can convert a
+// graph of BPF instructions into a well-formed stream of BPF instructions.
+// Most notably, it ensures that jumps are always forward and don't exceed
+// the limit of 255 instructions imposed by the instruction set.
+//
+// Callers would typically create a new CodeGen object and then use it to
+// build a DAG of Instructions. They'll eventually call Compile() to convert
+// this DAG to a Program.
+//
+// CodeGen gen;
+// Instruction *allow, *branch, *dag;
+//
+// allow =
+// gen.MakeInstruction(BPF_RET+BPF_K,
+// ErrorCode(ErrorCode::ERR_ALLOWED).err()));
+// branch =
+// gen.MakeInstruction(BPF_JMP+BPF_EQ+BPF_K, __NR_getpid,
+// Trap(GetPidHandler, NULL), allow);
+// dag =
+// gen.MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+// offsetof(struct arch_seccomp_data, nr), branch);
+//
+// // Simplified code follows; in practice, it is important to avoid calling
+// // any C++ destructors after starting the sandbox.
+// CodeGen::Program program;
+// gen.Compile(dag, program);
+// const struct sock_fprog prog = {
+// static_cast<unsigned short>(program->size()), &program[0] };
+// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+//
+class SANDBOX_EXPORT CodeGen {
+ public:
+ // A vector of BPF instructions that need to be installed as a filter
+ // program in the kernel.
+ typedef std::vector<struct sock_filter> Program;
+
+ CodeGen();
+ ~CodeGen();
+
+ // This is a helper method that can be used for debugging purposes. It is
+ // not normally called.
+ static void PrintProgram(const Program& program);
+
+ // Create a new instruction. Instructions form a DAG. The instruction objects
+ // are owned by the CodeGen object. They do not need to be explicitly
+ // deleted.
+ // For details on the possible parameters refer to <linux/filter.h>
+ Instruction* MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* next = nullptr);
+ Instruction* MakeInstruction(uint16_t code,
+ uint32_t k,
+ Instruction* jt,
+ Instruction* jf);
+
+ // Compiles the graph of instructions into a BPF program that can be passed
+ // to the kernel. Please note that this function modifies the graph in place
+ // and must therefore only be called once per graph.
+ void Compile(Instruction* instructions, Program* program);
+
+ private:
+ friend class CodeGenUnittestHelper;
+
+ // Find all the instructions that are the target of BPF_JMPs.
+ void FindBranchTargets(const Instruction& instructions,
+ BranchTargets* branch_targets);
+
+ // Combine instructions between "head" and "tail" into a new basic block.
+ // Basic blocks are defined as sequences of instructions whose only branch
+ // target is the very first instruction; furthermore, any BPF_JMP or BPF_RET
+ // instruction must be at the very end of the basic block.
+ BasicBlock* MakeBasicBlock(Instruction* head, Instruction* tail);
+
+ // Creates a basic block and adds it to "basic_blocks"; sets "first_block"
+ // if it is still NULL.
+ void AddBasicBlock(Instruction* head,
+ Instruction* tail,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks* basic_blocks,
+ BasicBlock** first_block);
+
+ // Cuts the DAG of instructions into basic blocks.
+ BasicBlock* CutGraphIntoBasicBlocks(Instruction* instructions,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks* blocks);
+
+ // Find common tail sequences of basic blocks and coalesce them.
+ void MergeTails(TargetsToBlocks* blocks);
+
+ // For each basic block, compute the number of incoming branches.
+ void ComputeIncomingBranches(BasicBlock* block,
+ const TargetsToBlocks& targets_to_blocks,
+ IncomingBranches* incoming_branches);
+
+ // Topologically sort the basic blocks so that all jumps are forward jumps.
+ // This is a requirement for any well-formed BPF program.
+ void TopoSortBasicBlocks(BasicBlock* first_block,
+ const TargetsToBlocks& blocks,
+ BasicBlocks* basic_blocks);
+
+ // Convert jt_ptr_ and jf_ptr_ fields in BPF_JMP instructions to valid
+ // jt_ and jf_ jump offsets. This can result in BPF_JA instructions being
+ // inserted, if we need to jump over more than 256 instructions.
+ void ComputeRelativeJumps(BasicBlocks* basic_blocks,
+ const TargetsToBlocks& targets_to_blocks);
+
+ // Concatenate instructions from all basic blocks into a BPF program that
+ // can be passed to the kernel.
+ void ConcatenateBasicBlocks(const BasicBlocks&, Program* program);
+
+ // We stick all instructions and basic blocks into pools that get destroyed
+ // when the CodeGen object is destroyed. This way, we neither need to worry
+ // about explicitly managing ownership, nor do we need to worry about using
+ // smart pointers in the presence of circular references.
+ Instructions instructions_;
+ BasicBlocks basic_blocks_;
+
+ // Compile() must only ever be called once as it makes destructive changes
+ // to the DAG.
+ bool compiled_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
diff --git a/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/sandbox/linux/seccomp-bpf/codegen_unittest.cc
new file mode 100644
index 0000000..d4760a5
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/codegen_unittest.cc
@@ -0,0 +1,534 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+
+#include <errno.h>
+#include <linux/filter.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/basicblock.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/instruction.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+// We want to access some of the private methods in the code generator. We
+// do so by defining a "friend" that makes these methods public for us.
+class CodeGenUnittestHelper : public CodeGen {
+ public:
+ void FindBranchTargets(const Instruction& instructions,
+ BranchTargets* branch_targets) {
+ CodeGen::FindBranchTargets(instructions, branch_targets);
+ }
+
+ BasicBlock* CutGraphIntoBasicBlocks(Instruction* insns,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks* blocks) {
+ return CodeGen::CutGraphIntoBasicBlocks(insns, branch_targets, blocks);
+ }
+
+ void MergeTails(TargetsToBlocks* blocks) { CodeGen::MergeTails(blocks); }
+};
+
+enum { NO_FLAGS = 0x0000, HAS_MERGEABLE_TAILS = 0x0001, };
+
+Instruction* SampleProgramOneInstruction(CodeGen* codegen, int* flags) {
+ // Create the most basic valid BPF program:
+ // RET 0
+ *flags = NO_FLAGS;
+ return codegen->MakeInstruction(BPF_RET + BPF_K, 0);
+}
+
+Instruction* SampleProgramSimpleBranch(CodeGen* codegen, int* flags) {
+ // Create a program with a single branch:
+ // JUMP if eq 42 then $0 else $1
+ // 0: RET 1
+ // 1: RET 0
+ *flags = NO_FLAGS;
+ return codegen->MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 42,
+ codegen->MakeInstruction(BPF_RET + BPF_K, 1),
+ codegen->MakeInstruction(BPF_RET + BPF_K, 0));
+}
+
+Instruction* SampleProgramAtypicalBranch(CodeGen* codegen, int* flags) {
+ // Create a program with a single branch:
+ // JUMP if eq 42 then $0 else $0
+ // 0: RET 0
+
+ // N.B.: As the instructions in both sides of the branch are already
+ // the same object, we do not actually have any "mergeable" branches.
+ // This needs to be reflected in our choice of "flags".
+ *flags = NO_FLAGS;
+
+ Instruction* ret = codegen->MakeInstruction(
+ BPF_RET + BPF_K, 0);
+ return codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
+}
+
+Instruction* SampleProgramComplex(CodeGen* codegen, int* flags) {
+ // Creates a basic BPF program that we'll use to test some of the code:
+ // JUMP if eq 42 the $0 else $1 (insn6)
+ // 0: LD 23 (insn5)
+ // 1: JUMP if eq 42 then $2 else $4 (insn4)
+ // 2: JUMP to $3 (insn2)
+ // 3: LD 42 (insn1)
+ // RET 42 (insn0)
+ // 4: LD 42 (insn3)
+ // RET 42 (insn3+)
+ *flags = HAS_MERGEABLE_TAILS;
+
+ Instruction* insn0 = codegen->MakeInstruction(BPF_RET + BPF_K, 42);
+ SANDBOX_ASSERT(insn0);
+ SANDBOX_ASSERT(insn0->code == BPF_RET + BPF_K);
+ SANDBOX_ASSERT(insn0->next == NULL);
+
+ Instruction* insn1 =
+ codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
+ SANDBOX_ASSERT(insn1);
+ SANDBOX_ASSERT(insn1->code == BPF_LD + BPF_W + BPF_ABS);
+ SANDBOX_ASSERT(insn1->k == 42);
+ SANDBOX_ASSERT(insn1->next == insn0);
+
+ Instruction* insn2 = codegen->MakeInstruction(BPF_JMP + BPF_JA, 0, insn1);
+ SANDBOX_ASSERT(insn2);
+ SANDBOX_ASSERT(insn2->code == BPF_JMP + BPF_JA);
+ SANDBOX_ASSERT(insn2->jt_ptr == insn1);
+
+ // We explicitly duplicate instructions so that MergeTails() can coalesce
+ // them later.
+ Instruction* insn3 = codegen->MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ 42,
+ codegen->MakeInstruction(BPF_RET + BPF_K, 42));
+
+ Instruction* insn4 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3);
+ SANDBOX_ASSERT(insn4);
+ SANDBOX_ASSERT(insn4->code == BPF_JMP + BPF_JEQ + BPF_K);
+ SANDBOX_ASSERT(insn4->k == 42);
+ SANDBOX_ASSERT(insn4->jt_ptr == insn2);
+ SANDBOX_ASSERT(insn4->jf_ptr == insn3);
+
+ Instruction* insn5 =
+ codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
+ SANDBOX_ASSERT(insn5);
+ SANDBOX_ASSERT(insn5->code == BPF_LD + BPF_W + BPF_ABS);
+ SANDBOX_ASSERT(insn5->k == 23);
+ SANDBOX_ASSERT(insn5->next == insn4);
+
+ // Force a basic block that ends in neither a jump instruction nor a return
+ // instruction. It only contains "insn5". This exercises one of the less
+ // common code paths in the topo-sort algorithm.
+ // This also gives us a diamond-shaped pattern in our graph, which stresses
+ // another aspect of the topo-sort algorithm (namely, the ability to
+ // correctly count the incoming branches for subtrees that are not disjunct).
+ Instruction* insn6 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
+
+ return insn6;
+}
+
+Instruction* SampleProgramConfusingTails(CodeGen* codegen, int* flags) {
+ // This simple program demonstrates https://crbug.com/351103/
+ // The two "LOAD 0" instructions are blocks of their own. MergeTails() could
+ // be tempted to merge them since they are the same. However, they are
+ // not mergeable because they fall-through to non semantically equivalent
+ // blocks.
+ // Without the fix for this bug, this program should trigger the check in
+ // CompileAndCompare: the serialized graphs from the program and its compiled
+ // version will differ.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0
+ // 7) RET 1
+ *flags = NO_FLAGS;
+
+ Instruction* i7 = codegen->MakeInstruction(BPF_RET + BPF_K, 1);
+ Instruction* i6 = codegen->MakeInstruction(BPF_RET + BPF_K, 0);
+ Instruction* i5 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ Instruction* i3 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ Instruction* i1 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ return i0;
+}
+
+Instruction* SampleProgramConfusingTailsBasic(CodeGen* codegen, int* flags) {
+ // Without the fix for https://crbug.com/351103/, (see
+ // SampleProgramConfusingTails()), this would generate a cyclic graph and
+ // crash as the two "LOAD 0" instructions would get merged.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) RET 1
+ *flags = NO_FLAGS;
+
+ Instruction* i5 = codegen->MakeInstruction(BPF_RET + BPF_K, 1);
+ Instruction* i4 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ Instruction* i3 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ Instruction* i2 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ Instruction* i1 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ return i0;
+}
+
+Instruction* SampleProgramConfusingTailsMergeable(CodeGen* codegen,
+ int* flags) {
+ // This is similar to SampleProgramConfusingTails(), except that
+ // instructions 2 and 4 are now RET instructions.
+ // In PointerCompare(), this exercises the path where two blocks are of the
+ // same length and identical and the last instruction is a JMP or RET, so the
+ // following blocks don't need to be looked at and the blocks are mergeable.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) RET 42
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) RET 42
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0
+ // 7) RET 1
+ *flags = HAS_MERGEABLE_TAILS;
+
+ Instruction* i7 = codegen->MakeInstruction(BPF_RET + BPF_K, 1);
+ Instruction* i6 = codegen->MakeInstruction(BPF_RET + BPF_K, 0);
+ Instruction* i5 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ Instruction* i4 = codegen->MakeInstruction(BPF_RET + BPF_K, 42);
+ Instruction* i3 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ Instruction* i2 = codegen->MakeInstruction(BPF_RET + BPF_K, 42);
+ Instruction* i1 =
+ codegen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ Instruction* i0 = codegen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ return i0;
+}
+void ForAllPrograms(void (*test)(CodeGenUnittestHelper*, Instruction*, int)) {
+ Instruction* (*function_table[])(CodeGen* codegen, int* flags) = {
+ SampleProgramOneInstruction,
+ SampleProgramSimpleBranch,
+ SampleProgramAtypicalBranch,
+ SampleProgramComplex,
+ SampleProgramConfusingTails,
+ SampleProgramConfusingTailsBasic,
+ SampleProgramConfusingTailsMergeable,
+ };
+
+ for (size_t i = 0; i < arraysize(function_table); ++i) {
+ CodeGenUnittestHelper codegen;
+ int flags = NO_FLAGS;
+ Instruction *prg = function_table[i](&codegen, &flags);
+ test(&codegen, prg, flags);
+ }
+}
+
+void MakeInstruction(CodeGenUnittestHelper* codegen,
+ Instruction* program, int) {
+ // Nothing to do here
+}
+
+SANDBOX_TEST(CodeGen, MakeInstruction) {
+ ForAllPrograms(MakeInstruction);
+}
+
+void FindBranchTargets(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
+ BranchTargets branch_targets;
+ codegen->FindBranchTargets(*prg, &branch_targets);
+
+ // Verifying the general properties that should be true for every
+ // well-formed BPF program.
+ // Perform a depth-first traversal of the BPF program an verify that all
+ // targets of BPF_JMP instructions are represented in the "branch_targets".
+ // At the same time, compute a set of both the branch targets and all the
+ // instructions in the program.
+ std::vector<Instruction*> stack;
+ std::set<Instruction*> all_instructions;
+ std::set<Instruction*> target_instructions;
+ BranchTargets::const_iterator end = branch_targets.end();
+ for (Instruction* insn = prg;;) {
+ all_instructions.insert(insn);
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ target_instructions.insert(insn->jt_ptr);
+ SANDBOX_ASSERT(insn->jt_ptr != NULL);
+ SANDBOX_ASSERT(branch_targets.find(insn->jt_ptr) != end);
+ if (BPF_OP(insn->code) != BPF_JA) {
+ target_instructions.insert(insn->jf_ptr);
+ SANDBOX_ASSERT(insn->jf_ptr != NULL);
+ SANDBOX_ASSERT(branch_targets.find(insn->jf_ptr) != end);
+ stack.push_back(insn->jf_ptr);
+ }
+ insn = insn->jt_ptr;
+ } else if (BPF_CLASS(insn->code) == BPF_RET) {
+ SANDBOX_ASSERT(insn->next == NULL);
+ if (stack.empty()) {
+ break;
+ }
+ insn = stack.back();
+ stack.pop_back();
+ } else {
+ SANDBOX_ASSERT(insn->next != NULL);
+ insn = insn->next;
+ }
+ }
+ SANDBOX_ASSERT(target_instructions.size() == branch_targets.size());
+
+ // We can now subtract the set of the branch targets from the set of all
+ // instructions. This gives us a set with the instructions that nobody
+ // ever jumps to. Verify that they are no included in the
+ // "branch_targets" that FindBranchTargets() computed for us.
+ Instructions non_target_instructions(all_instructions.size() -
+ target_instructions.size());
+ set_difference(all_instructions.begin(),
+ all_instructions.end(),
+ target_instructions.begin(),
+ target_instructions.end(),
+ non_target_instructions.begin());
+ for (Instructions::const_iterator iter = non_target_instructions.begin();
+ iter != non_target_instructions.end();
+ ++iter) {
+ SANDBOX_ASSERT(branch_targets.find(*iter) == end);
+ }
+}
+
+SANDBOX_TEST(CodeGen, FindBranchTargets) { ForAllPrograms(FindBranchTargets); }
+
+void CutGraphIntoBasicBlocks(CodeGenUnittestHelper* codegen,
+ Instruction* prg,
+ int) {
+ BranchTargets branch_targets;
+ codegen->FindBranchTargets(*prg, &branch_targets);
+ TargetsToBlocks all_blocks;
+ BasicBlock* first_block =
+ codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
+ SANDBOX_ASSERT(first_block != NULL);
+ SANDBOX_ASSERT(first_block->instructions.size() > 0);
+ Instruction* first_insn = first_block->instructions[0];
+
+ // Basic blocks are supposed to start with a branch target and end with
+ // either a jump or a return instruction. It can also end, if the next
+ // instruction forms the beginning of a new basic block. There should be
+ // no other jumps or return instructions in the middle of a basic block.
+ for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin();
+ bb_iter != all_blocks.end();
+ ++bb_iter) {
+ BasicBlock* bb = bb_iter->second;
+ SANDBOX_ASSERT(bb != NULL);
+ SANDBOX_ASSERT(bb->instructions.size() > 0);
+ Instruction* insn = bb->instructions[0];
+ SANDBOX_ASSERT(insn == first_insn ||
+ branch_targets.find(insn) != branch_targets.end());
+ for (Instructions::const_iterator insn_iter = bb->instructions.begin();;) {
+ insn = *insn_iter;
+ if (++insn_iter != bb->instructions.end()) {
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_JMP);
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_RET);
+ } else {
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) == BPF_JMP ||
+ BPF_CLASS(insn->code) == BPF_RET ||
+ branch_targets.find(insn->next) != branch_targets.end());
+ break;
+ }
+ SANDBOX_ASSERT(branch_targets.find(*insn_iter) == branch_targets.end());
+ }
+ }
+}
+
+SANDBOX_TEST(CodeGen, CutGraphIntoBasicBlocks) {
+ ForAllPrograms(CutGraphIntoBasicBlocks);
+}
+
+void MergeTails(CodeGenUnittestHelper* codegen, Instruction* prg, int flags) {
+ BranchTargets branch_targets;
+ codegen->FindBranchTargets(*prg, &branch_targets);
+ TargetsToBlocks all_blocks;
+ BasicBlock* first_block =
+ codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
+
+ // The shape of our graph and thus the function of our program should
+ // still be unchanged after we run MergeTails(). We verify this by
+ // serializing the graph and verifying that it is still the same.
+ // We also verify that at least some of the edges changed because of
+ // tail merging.
+ std::string graph[2];
+ std::string edges[2];
+
+ // The loop executes twice. After the first run, we call MergeTails() on
+ // our graph.
+ for (int i = 0;;) {
+ // Traverse the entire program in depth-first order.
+ std::vector<BasicBlock*> stack;
+ for (BasicBlock* bb = first_block;;) {
+ // Serialize the instructions in this basic block. In general, we only
+ // need to serialize "code" and "k"; except for a BPF_JA instruction
+ // where "k" isn't set.
+ // The stream of instructions should be unchanged after MergeTails().
+ for (Instructions::const_iterator iter = bb->instructions.begin();
+ iter != bb->instructions.end();
+ ++iter) {
+ graph[i].append(reinterpret_cast<char*>(&(*iter)->code),
+ sizeof((*iter)->code));
+ if (BPF_CLASS((*iter)->code) != BPF_JMP ||
+ BPF_OP((*iter)->code) != BPF_JA) {
+ graph[i].append(reinterpret_cast<char*>(&(*iter)->k),
+ sizeof((*iter)->k));
+ }
+ }
+
+ // Also serialize the addresses the basic blocks as we encounter them.
+ // This will change as basic blocks are coalesed by MergeTails().
+ edges[i].append(reinterpret_cast<char*>(&bb), sizeof(bb));
+
+ // Depth-first traversal of the graph. We only ever need to look at the
+ // very last instruction in the basic block, as that is the only one that
+ // can change code flow.
+ Instruction* insn = bb->instructions.back();
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // For jump instructions, we need to remember the "false" branch while
+ // traversing the "true" branch. This is not necessary for BPF_JA which
+ // only has a single branch.
+ if (BPF_OP(insn->code) != BPF_JA) {
+ stack.push_back(all_blocks[insn->jf_ptr]);
+ }
+ bb = all_blocks[insn->jt_ptr];
+ } else if (BPF_CLASS(insn->code) == BPF_RET) {
+ // After a BPF_RET, see if we need to back track.
+ if (stack.empty()) {
+ break;
+ }
+ bb = stack.back();
+ stack.pop_back();
+ } else {
+ // For "normal" instructions, just follow to the next basic block.
+ bb = all_blocks[insn->next];
+ }
+ }
+
+ // Our loop runs exactly two times.
+ if (++i > 1) {
+ break;
+ }
+ codegen->MergeTails(&all_blocks);
+ }
+ SANDBOX_ASSERT(graph[0] == graph[1]);
+ if (flags & HAS_MERGEABLE_TAILS) {
+ SANDBOX_ASSERT(edges[0] != edges[1]);
+ } else {
+ SANDBOX_ASSERT(edges[0] == edges[1]);
+ }
+}
+
+SANDBOX_TEST(CodeGen, MergeTails) {
+ ForAllPrograms(MergeTails);
+}
+
+void CompileAndCompare(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
+ // TopoSortBasicBlocks() has internal checks that cause it to fail, if it
+ // detects a problem. Typically, if anything goes wrong, this looks to the
+ // TopoSort algorithm as if there had been cycles in the input data.
+ // This provides a pretty good unittest.
+ // We hand-crafted the program returned by SampleProgram() to exercise
+ // several of the more interesting code-paths. See comments in
+ // SampleProgram() for details.
+ // In addition to relying on the internal consistency checks in the compiler,
+ // we also serialize the graph and the resulting BPF program and compare
+ // them. With the exception of BPF_JA instructions that might have been
+ // inserted, both instruction streams should be equivalent.
+ // As Compile() modifies the instructions, we have to serialize the graph
+ // before calling Compile().
+ std::string source;
+ Instructions source_stack;
+ for (const Instruction* insn = prg, *next; insn; insn = next) {
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ if (BPF_OP(insn->code) == BPF_JA) {
+ // Do not serialize BPF_JA instructions (see above).
+ next = insn->jt_ptr;
+ continue;
+ } else {
+ source_stack.push_back(insn->jf_ptr);
+ next = insn->jt_ptr;
+ }
+ } else if (BPF_CLASS(insn->code) == BPF_RET) {
+ if (source_stack.empty()) {
+ next = NULL;
+ } else {
+ next = source_stack.back();
+ source_stack.pop_back();
+ }
+ } else {
+ next = insn->next;
+ }
+ // Only serialize "code" and "k". That's all the information we need to
+ // compare. The rest of the information is encoded in the order of
+ // instructions.
+ source.append(reinterpret_cast<const char*>(&insn->code),
+ sizeof(insn->code));
+ source.append(reinterpret_cast<const char*>(&insn->k), sizeof(insn->k));
+ }
+
+ // Compile the program
+ CodeGen::Program bpf;
+ codegen->Compile(prg, &bpf);
+
+ // Serialize the resulting BPF instructions.
+ std::string assembly;
+ std::vector<int> assembly_stack;
+ for (int idx = 0; idx >= 0;) {
+ SANDBOX_ASSERT(idx < (int)bpf.size());
+ struct sock_filter& insn = bpf[idx];
+ if (BPF_CLASS(insn.code) == BPF_JMP) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ // Do not serialize BPF_JA instructions (see above).
+ idx += insn.k + 1;
+ continue;
+ } else {
+ assembly_stack.push_back(idx + insn.jf + 1);
+ idx += insn.jt + 1;
+ }
+ } else if (BPF_CLASS(insn.code) == BPF_RET) {
+ if (assembly_stack.empty()) {
+ idx = -1;
+ } else {
+ idx = assembly_stack.back();
+ assembly_stack.pop_back();
+ }
+ } else {
+ ++idx;
+ }
+ // Serialize the same information that we serialized before compilation.
+ assembly.append(reinterpret_cast<char*>(&insn.code), sizeof(insn.code));
+ assembly.append(reinterpret_cast<char*>(&insn.k), sizeof(insn.k));
+ }
+ SANDBOX_ASSERT(source == assembly);
+}
+
+SANDBOX_TEST(CodeGen, All) {
+ ForAllPrograms(CompileAndCompare);
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/die.cc b/sandbox/linux/seccomp-bpf/die.cc
new file mode 100644
index 0000000..777c9d1
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/die.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+namespace sandbox {
+
+void Die::ExitGroup() {
+ // exit_group() should exit our program. After all, it is defined as a
+ // function that doesn't return. But things can theoretically go wrong.
+ // Especially, since we are dealing with system call filters. Continuing
+ // execution would be very bad in most cases where ExitGroup() gets called.
+ // So, we'll try a few other strategies too.
+ Syscall::Call(__NR_exit_group, 1);
+
+ // We have no idea what our run-time environment looks like. So, signal
+ // handlers might or might not do the right thing. Try to reset settings
+ // to a defined state; but we have not way to verify whether we actually
+ // succeeded in doing so. Nonetheless, triggering a fatal signal could help
+ // us terminate.
+ signal(SIGSEGV, SIG_DFL);
+ Syscall::Call(__NR_prctl, PR_SET_DUMPABLE, (void*)0, (void*)0, (void*)0);
+ if (*(volatile char*)0) {
+ }
+
+ // If there is no way for us to ask for the program to exit, the next
+ // best thing we can do is to loop indefinitely. Maybe, somebody will notice
+ // and file a bug...
+ // We in fact retry the system call inside of our loop so that it will
+ // stand out when somebody tries to diagnose the problem by using "strace".
+ for (;;) {
+ Syscall::Call(__NR_exit_group, 1);
+ }
+}
+
+void Die::SandboxDie(const char* msg, const char* file, int line) {
+ if (simple_exit_) {
+ LogToStderr(msg, file, line);
+ } else {
+ logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
+ }
+ ExitGroup();
+}
+
+void Die::RawSandboxDie(const char* msg) {
+ if (!msg)
+ msg = "";
+ RAW_LOG(FATAL, msg);
+ ExitGroup();
+}
+
+void Die::SandboxInfo(const char* msg, const char* file, int line) {
+ if (!suppress_info_) {
+ logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
+ }
+}
+
+void Die::LogToStderr(const char* msg, const char* file, int line) {
+ if (msg) {
+ char buf[40];
+ snprintf(buf, sizeof(buf), "%d", line);
+ std::string s = std::string(file) + ":" + buf + ":" + msg + "\n";
+
+ // No need to loop. Short write()s are unlikely and if they happen we
+ // probably prefer them over a loop that blocks.
+ ignore_result(
+ HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length())));
+ }
+}
+
+bool Die::simple_exit_ = false;
+bool Die::suppress_info_ = false;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/die.h b/sandbox/linux/seccomp-bpf/die.h
new file mode 100644
index 0000000..b3f3f72
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/die.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is the main API for using this file. Prints a error message and
+// exits with a fatal error. This is not async-signal safe.
+#define SANDBOX_DIE(m) sandbox::Die::SandboxDie(m, __FILE__, __LINE__)
+
+// An async signal safe version of the same API. Won't print the filename
+// and line numbers.
+#define RAW_SANDBOX_DIE(m) sandbox::Die::RawSandboxDie(m)
+
+// Adds an informational message to the log file or stderr as appropriate.
+#define SANDBOX_INFO(m) sandbox::Die::SandboxInfo(m, __FILE__, __LINE__)
+
+class SANDBOX_EXPORT Die {
+ public:
+ // Terminate the program, even if the current sandbox policy prevents some
+ // of the more commonly used functions used for exiting.
+ // Most users would want to call SANDBOX_DIE() instead, as it logs extra
+ // information. But calling ExitGroup() is correct and in some rare cases
+ // preferable. So, we make it part of the public API.
+ static void ExitGroup() __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_DIE(). There is normally no reason
+ // to call it directly unless you are defining your own exiting macro.
+ static void SandboxDie(const char* msg, const char* file, int line)
+ __attribute__((noreturn));
+
+ static void RawSandboxDie(const char* msg) __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_INFO(). There is normally no reason
+ // to call it directly unless you are defining your own logging macro.
+ static void SandboxInfo(const char* msg, const char* file, int line);
+
+ // Writes a message to stderr. Used as a fall-back choice, if we don't have
+ // any other way to report an error.
+ static void LogToStderr(const char* msg, const char* file, int line);
+
+ // We generally want to run all exit handlers. This means, on SANDBOX_DIE()
+ // we should be calling LOG(FATAL). But there are some situations where
+ // we just need to print a message and then terminate. This would typically
+ // happen in cases where we consume the error message internally (e.g. in
+ // unit tests or in the supportsSeccompSandbox() method).
+ static void EnableSimpleExit() { simple_exit_ = true; }
+
+ // Sometimes we need to disable all informational messages (e.g. from within
+ // unittests).
+ static void SuppressInfoMessages(bool flag) { suppress_info_ = flag; }
+
+ private:
+ static bool simple_exit_;
+ static bool suppress_info_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Die);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
diff --git a/sandbox/linux/seccomp-bpf/errorcode.cc b/sandbox/linux/seccomp-bpf/errorcode.cc
new file mode 100644
index 0000000..ebae130
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+
+namespace sandbox {
+
+ErrorCode::ErrorCode() : error_type_(ET_INVALID), err_(SECCOMP_RET_INVALID) {
+}
+
+ErrorCode::ErrorCode(int err) {
+ switch (err) {
+ case ERR_ALLOWED:
+ err_ = SECCOMP_RET_ALLOW;
+ error_type_ = ET_SIMPLE;
+ break;
+ case ERR_MIN_ERRNO... ERR_MAX_ERRNO:
+ err_ = SECCOMP_RET_ERRNO + err;
+ error_type_ = ET_SIMPLE;
+ break;
+ default:
+ if ((err & ~SECCOMP_RET_DATA) == ERR_TRACE) {
+ err_ = SECCOMP_RET_TRACE + (err & SECCOMP_RET_DATA);
+ error_type_ = ET_SIMPLE;
+ break;
+ }
+ SANDBOX_DIE("Invalid use of ErrorCode object");
+ }
+}
+
+ErrorCode::ErrorCode(uint16_t trap_id,
+ Trap::TrapFnc fnc,
+ const void* aux,
+ bool safe)
+ : error_type_(ET_TRAP),
+ fnc_(fnc),
+ aux_(const_cast<void*>(aux)),
+ safe_(safe),
+ err_(SECCOMP_RET_TRAP + trap_id) {
+}
+
+ErrorCode::ErrorCode(int argno,
+ ArgType width,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode* passed,
+ const ErrorCode* failed)
+ : error_type_(ET_COND),
+ mask_(mask),
+ value_(value),
+ argno_(argno),
+ width_(width),
+ passed_(passed),
+ failed_(failed),
+ err_(SECCOMP_RET_INVALID) {
+}
+
+bool ErrorCode::Equals(const ErrorCode& err) const {
+ if (error_type_ == ET_INVALID || err.error_type_ == ET_INVALID) {
+ SANDBOX_DIE("Dereferencing invalid ErrorCode");
+ }
+ if (error_type_ != err.error_type_) {
+ return false;
+ }
+ if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
+ return err_ == err.err_;
+ } else if (error_type_ == ET_COND) {
+ return mask_ == err.mask_ && value_ == err.value_ && argno_ == err.argno_ &&
+ width_ == err.width_ && passed_->Equals(*err.passed_) &&
+ failed_->Equals(*err.failed_);
+ } else {
+ SANDBOX_DIE("Corrupted ErrorCode");
+ }
+}
+
+bool ErrorCode::LessThan(const ErrorCode& err) const {
+ // Implementing a "LessThan()" operator allows us to use ErrorCode objects
+ // as keys in STL containers; most notably, it also allows us to put them
+ // into std::set<>. Actual ordering is not important as long as it is
+ // deterministic.
+ if (error_type_ == ET_INVALID || err.error_type_ == ET_INVALID) {
+ SANDBOX_DIE("Dereferencing invalid ErrorCode");
+ }
+ if (error_type_ != err.error_type_) {
+ return error_type_ < err.error_type_;
+ } else {
+ if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
+ return err_ < err.err_;
+ } else if (error_type_ == ET_COND) {
+ if (mask_ != err.mask_) {
+ return mask_ < err.mask_;
+ } else if (value_ != err.value_) {
+ return value_ < err.value_;
+ } else if (argno_ != err.argno_) {
+ return argno_ < err.argno_;
+ } else if (width_ != err.width_) {
+ return width_ < err.width_;
+ } else if (!passed_->Equals(*err.passed_)) {
+ return passed_->LessThan(*err.passed_);
+ } else if (!failed_->Equals(*err.failed_)) {
+ return failed_->LessThan(*err.failed_);
+ } else {
+ return false;
+ }
+ } else {
+ SANDBOX_DIE("Corrupted ErrorCode");
+ }
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
new file mode 100644
index 0000000..fb86fe8
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -0,0 +1,199 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
+
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class PolicyCompiler;
+}
+
+// This class holds all the possible values that can be returned by a sandbox
+// policy.
+// We can either wrap a symbolic ErrorCode (i.e. ERR_XXX enum values), an
+// errno value (in the range 0..4095), a pointer to a TrapFnc callback
+// handling a SECCOMP_RET_TRAP trap, or a complex constraint.
+// All of the commonly used values are stored in the "err_" field. So, code
+// that is using the ErrorCode class typically operates on a single 32bit
+// field.
+class SANDBOX_EXPORT ErrorCode {
+ public:
+ enum {
+ // Allow this system call. The value of ERR_ALLOWED is pretty much
+ // completely arbitrary. But we want to pick it so that is is unlikely
+ // to be passed in accidentally, when the user intended to return an
+ // "errno" (see below) value instead.
+ ERR_ALLOWED = 0x04000000,
+
+ // If the progress is being ptraced with PTRACE_O_TRACESECCOMP, then the
+ // tracer will be notified of a PTRACE_EVENT_SECCOMP and allowed to change
+ // or skip the system call. The lower 16 bits of err will be available to
+ // the tracer via PTRACE_GETEVENTMSG.
+ ERR_TRACE = 0x08000000,
+
+ // Deny the system call with a particular "errno" value.
+ // N.B.: It is also possible to return "0" here. That would normally
+ // indicate success, but it won't actually run the system call.
+ // This is very different from return ERR_ALLOWED.
+ ERR_MIN_ERRNO = 0,
+#if defined(__mips__)
+ // MIPS only supports errno up to 1133
+ ERR_MAX_ERRNO = 1133,
+#else
+ // TODO(markus): Android only supports errno up to 255
+ // (crbug.com/181647).
+ ERR_MAX_ERRNO = 4095,
+#endif
+ };
+
+ // While BPF filter programs always operate on 32bit quantities, the kernel
+ // always sees system call arguments as 64bit values. This statement is true
+ // no matter whether the host system is natively operating in 32bit or 64bit.
+ // The BPF compiler hides the fact that BPF instructions cannot directly
+ // access 64bit quantities. But policies are still advised to specify whether
+ // a system call expects a 32bit or a 64bit quantity.
+ enum ArgType {
+ // When passed as an argument to SandboxBPF::Cond(), TP_32BIT requests that
+ // the conditional test should operate on the 32bit part of the system call
+ // argument.
+ // On 64bit architectures, this verifies that user space did not pass
+ // a 64bit value as an argument to the system call. If it did, that will be
+ // interpreted as an attempt at breaking the sandbox and results in the
+ // program getting terminated.
+ // In other words, only perform a 32bit test, if you are sure this
+ // particular system call would never legitimately take a 64bit
+ // argument.
+ // Implementation detail: TP_32BIT does two things. 1) it restricts the
+ // conditional test to operating on the LSB only, and 2) it adds code to
+ // the BPF filter program verifying that the MSB the kernel received from
+ // user space is either 0, or 0xFFFFFFFF; the latter is acceptable, iff bit
+ // 31 was set in the system call argument. It deals with 32bit arguments
+ // having been sign extended.
+ TP_32BIT,
+
+ // When passed as an argument to SandboxBPF::Cond(), TP_64BIT requests that
+ // the conditional test should operate on the full 64bit argument. It is
+ // generally harmless to perform a 64bit test on 32bit systems, as the
+ // kernel will always see the top 32 bits of all arguments as zero'd out.
+ // This approach has the desirable property that for tests of pointer
+ // values, we can always use TP_64BIT no matter the host architecture.
+ // But of course, that also means, it is possible to write conditional
+ // policies that turn into no-ops on 32bit systems; this is by design.
+ TP_64BIT,
+ };
+
+ // Deprecated.
+ enum Operation {
+ // Test whether the system call argument is equal to the operand.
+ OP_EQUAL,
+
+ // Tests a system call argument against a bit mask.
+ // The "ALL_BITS" variant performs this test: "arg & mask == mask"
+ // This implies that a mask of zero always results in a passing test.
+ // The "ANY_BITS" variant performs this test: "arg & mask != 0"
+ // This implies that a mask of zero always results in a failing test.
+ OP_HAS_ALL_BITS,
+ OP_HAS_ANY_BITS,
+ };
+
+ enum ErrorType {
+ ET_INVALID,
+ ET_SIMPLE,
+ ET_TRAP,
+ ET_COND,
+ };
+
+ // We allow the default constructor, as it makes the ErrorCode class
+ // much easier to use. But if we ever encounter an invalid ErrorCode
+ // when compiling a BPF filter, we deliberately generate an invalid
+ // program that will get flagged both by our Verifier class and by
+ // the Linux kernel.
+ ErrorCode();
+ explicit ErrorCode(int err);
+
+ // For all practical purposes, ErrorCodes are treated as if they were
+ // structs. The copy constructor and assignment operator are trivial and
+ // we do not need to explicitly specify them.
+ // Most notably, it is in fact perfectly OK to directly copy the passed_ and
+ // failed_ field. They only ever get set by our private constructor, and the
+ // callers handle life-cycle management for these objects.
+
+ // Destructor
+ ~ErrorCode() {}
+
+ bool Equals(const ErrorCode& err) const;
+ bool LessThan(const ErrorCode& err) const;
+
+ uint32_t err() const { return err_; }
+ ErrorType error_type() const { return error_type_; }
+
+ bool safe() const { return safe_; }
+
+ uint64_t mask() const { return mask_; }
+ uint64_t value() const { return value_; }
+ int argno() const { return argno_; }
+ ArgType width() const { return width_; }
+ const ErrorCode* passed() const { return passed_; }
+ const ErrorCode* failed() const { return failed_; }
+
+ struct LessThan {
+ bool operator()(const ErrorCode& a, const ErrorCode& b) const {
+ return a.LessThan(b);
+ }
+ };
+
+ private:
+ friend bpf_dsl::PolicyCompiler;
+ friend class CodeGen;
+ friend class SandboxBPF;
+ friend class Trap;
+
+ // If we are wrapping a callback, we must assign a unique id. This id is
+ // how the kernel tells us which one of our different SECCOMP_RET_TRAP
+ // cases has been triggered.
+ ErrorCode(uint16_t trap_id, Trap::TrapFnc fnc, const void* aux, bool safe);
+
+ // Some system calls require inspection of arguments. This constructor
+ // allows us to specify additional constraints.
+ ErrorCode(int argno,
+ ArgType width,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode* passed,
+ const ErrorCode* failed);
+
+ ErrorType error_type_;
+
+ union {
+ // Fields needed for SECCOMP_RET_TRAP callbacks
+ struct {
+ Trap::TrapFnc fnc_; // Callback function and arg, if trap was
+ void* aux_; // triggered by the kernel's BPF filter.
+ bool safe_; // Keep sandbox active while calling fnc_()
+ };
+
+ // Fields needed when inspecting additional arguments.
+ struct {
+ uint64_t mask_; // Mask that we are comparing under.
+ uint64_t value_; // Value that we are comparing with.
+ int argno_; // Syscall arg number that we are inspecting.
+ ArgType width_; // Whether we are looking at a 32/64bit value.
+ const ErrorCode* passed_; // Value to be returned if comparison passed,
+ const ErrorCode* failed_; // or if it failed.
+ };
+ };
+
+ // 32bit field used for all possible types of ErrorCode values. This is
+ // the value that uniquely identifies any ErrorCode and it (typically) can
+ // be emitted directly into a BPF filter program.
+ uint32_t err_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
diff --git a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
new file mode 100644
index 0000000..41bd358
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+#include <errno.h>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+class DummyPolicy : public bpf_dsl::SandboxBPFDSLPolicy {
+ public:
+ DummyPolicy() {}
+ virtual ~DummyPolicy() {}
+
+ virtual bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override {
+ return bpf_dsl::Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DummyPolicy);
+};
+
+SANDBOX_TEST(ErrorCode, ErrnoConstructor) {
+ ErrorCode e0;
+ SANDBOX_ASSERT(e0.err() == SECCOMP_RET_INVALID);
+
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(e1.err() == SECCOMP_RET_ALLOW);
+
+ ErrorCode e2(EPERM);
+ SANDBOX_ASSERT(e2.err() == SECCOMP_RET_ERRNO + EPERM);
+
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e3 = compiler.Trap(NULL, NULL);
+ SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP);
+
+ uint16_t data = 0xdead;
+ ErrorCode e4(ErrorCode::ERR_TRACE + data);
+ SANDBOX_ASSERT(e4.err() == SECCOMP_RET_TRACE + data);
+}
+
+SANDBOX_DEATH_TEST(ErrorCode,
+ InvalidSeccompRetTrace,
+ DEATH_MESSAGE("Invalid use of ErrorCode object")) {
+ // Should die if the trace data does not fit in 16 bits.
+ ErrorCode e(ErrorCode::ERR_TRACE + (1 << 16));
+}
+
+SANDBOX_TEST(ErrorCode, Trap) {
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e0 = compiler.Trap(NULL, "a");
+ ErrorCode e1 = compiler.Trap(NULL, "b");
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 ==
+ (e1.err() & SECCOMP_RET_DATA));
+
+ ErrorCode e2 = compiler.Trap(NULL, "a");
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) ==
+ (e2.err() & SECCOMP_RET_DATA));
+}
+
+SANDBOX_TEST(ErrorCode, Equals) {
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ ErrorCode e2(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(e1.Equals(e1));
+ SANDBOX_ASSERT(e1.Equals(e2));
+ SANDBOX_ASSERT(e2.Equals(e1));
+
+ ErrorCode e3(EPERM);
+ SANDBOX_ASSERT(!e1.Equals(e3));
+
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e4 = compiler.Trap(NULL, "a");
+ ErrorCode e5 = compiler.Trap(NULL, "b");
+ ErrorCode e6 = compiler.Trap(NULL, "a");
+ SANDBOX_ASSERT(!e1.Equals(e4));
+ SANDBOX_ASSERT(!e3.Equals(e4));
+ SANDBOX_ASSERT(!e5.Equals(e4));
+ SANDBOX_ASSERT( e6.Equals(e4));
+}
+
+SANDBOX_TEST(ErrorCode, LessThan) {
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ ErrorCode e2(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(!e1.LessThan(e1));
+ SANDBOX_ASSERT(!e1.LessThan(e2));
+ SANDBOX_ASSERT(!e2.LessThan(e1));
+
+ ErrorCode e3(EPERM);
+ SANDBOX_ASSERT(!e1.LessThan(e3));
+ SANDBOX_ASSERT( e3.LessThan(e1));
+
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e4 = compiler.Trap(NULL, "a");
+ ErrorCode e5 = compiler.Trap(NULL, "b");
+ ErrorCode e6 = compiler.Trap(NULL, "a");
+ SANDBOX_ASSERT(e1.LessThan(e4));
+ SANDBOX_ASSERT(e3.LessThan(e4));
+ SANDBOX_ASSERT(e4.LessThan(e5));
+ SANDBOX_ASSERT(!e4.LessThan(e6));
+ SANDBOX_ASSERT(!e6.LessThan(e4));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/instruction.h b/sandbox/linux/seccomp-bpf/instruction.h
new file mode 100644
index 0000000..70b7791
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/instruction.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__
+
+#include <stdint.h>
+
+#include <cstddef>
+
+namespace sandbox {
+
+// The fields in this structure have the same meaning as the corresponding
+// fields in "struct sock_filter". See <linux/filter.h> for a lot more
+// detail.
+// code -- Opcode of the instruction. This is typically a bitwise
+// combination BPF_XXX values.
+// k -- Operand; BPF instructions take zero or one operands. Operands
+// are 32bit-wide constants, if present. They can be immediate
+// values (if BPF_K is present in "code_"), addresses (if BPF_ABS
+// is present in "code_"), or relative jump offsets (if BPF_JMP
+// and BPF_JA are present in "code_").
+// jt, jf -- all conditional jumps have a 8bit-wide jump offset that allows
+// jumps of up to 256 instructions forward. Conditional jumps are
+// identified by BPF_JMP in "code_", but the lack of BPF_JA.
+// Conditional jumps have a "t"rue and "f"alse branch.
+struct Instruction {
+ // Constructor for an non-jumping instruction or for an unconditional
+ // "always" jump.
+ Instruction(uint16_t c, uint32_t parm, Instruction* n)
+ : code(c), jt(0), jf(0), jt_ptr(NULL), jf_ptr(NULL), next(n), k(parm) {}
+
+ // Constructor for a conditional jump instruction.
+ Instruction(uint16_t c, uint32_t parm, Instruction* jt, Instruction* jf)
+ : code(c), jt(0), jf(0), jt_ptr(jt), jf_ptr(jf), next(NULL), k(parm) {}
+
+ uint16_t code;
+
+ // When code generation is complete, we will have computed relative
+ // branch targets that are in the range 0..255.
+ uint8_t jt, jf;
+
+ // While assembling the BPF program, we use pointers for branch targets.
+ // Once we have computed basic blocks, these pointers will be entered as
+ // keys in a TargetsToBlocks map and should no longer be dereferenced
+ // directly.
+ Instruction* jt_ptr, *jf_ptr;
+
+ // While assembling the BPF program, non-jumping instructions are linked
+ // by the "next_" pointer. This field is no longer needed when we have
+ // computed basic blocks.
+ Instruction* next;
+
+ uint32_t k;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__
diff --git a/sandbox/linux/seccomp-bpf/linux_seccomp.h b/sandbox/linux/seccomp-bpf/linux_seccomp.h
new file mode 100644
index 0000000..c13ef20
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/linux_seccomp.h
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
+
+// The Seccomp2 kernel ABI is not part of older versions of glibc.
+// As we can't break compilation with these versions of the library,
+// we explicitly define all missing symbols.
+// If we ever decide that we can now rely on system headers, the following
+// include files should be enabled:
+// #include <linux/audit.h>
+// #include <linux/seccomp.h>
+
+#include <asm/unistd.h>
+#include <linux/filter.h>
+
+#include <sys/cdefs.h>
+// Old Bionic versions do not have sys/user.h. The if can be removed once we no
+// longer need to support these old Bionic versions.
+// All x86_64 builds use a new enough bionic to have sys/user.h.
+#if !defined(__BIONIC__) || defined(__x86_64__)
+#include <sys/types.h> // Fix for gcc 4.7, make sure __uint16_t is defined.
+#include <sys/user.h>
+#if defined(__mips__)
+// sys/user.h in eglibc misses size_t definition
+#include <stddef.h>
+#endif
+#endif
+
+// For audit.h
+#ifndef EM_ARM
+#define EM_ARM 40
+#endif
+#ifndef EM_386
+#define EM_386 3
+#endif
+#ifndef EM_X86_64
+#define EM_X86_64 62
+#endif
+#ifndef EM_MIPS
+#define EM_MIPS 8
+#endif
+#ifndef EM_AARCH64
+#define EM_AARCH64 183
+#endif
+
+#ifndef __AUDIT_ARCH_64BIT
+#define __AUDIT_ARCH_64BIT 0x80000000
+#endif
+#ifndef __AUDIT_ARCH_LE
+#define __AUDIT_ARCH_LE 0x40000000
+#endif
+#ifndef AUDIT_ARCH_ARM
+#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_I386
+#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_X86_64
+#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_MIPSEL
+#define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_AARCH64
+#define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE)
+#endif
+
+// For prctl.h
+#ifndef PR_SET_SECCOMP
+#define PR_SET_SECCOMP 22
+#define PR_GET_SECCOMP 21
+#endif
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+#ifndef IPC_64
+#define IPC_64 0x0100
+#endif
+
+#ifndef BPF_MOD
+#define BPF_MOD 0x90
+#endif
+#ifndef BPF_XOR
+#define BPF_XOR 0xA0
+#endif
+
+// In order to build will older tool chains, we currently have to avoid
+// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
+// our own definitions of the seccomp kernel ABI.
+#ifndef SECCOMP_MODE_FILTER
+#define SECCOMP_MODE_DISABLED 0
+#define SECCOMP_MODE_STRICT 1
+#define SECCOMP_MODE_FILTER 2 // User user-supplied filter
+#endif
+
+#ifndef SECCOMP_SET_MODE_STRICT
+#define SECCOMP_SET_MODE_STRICT 0
+#endif
+#ifndef SECCOMP_SET_MODE_FILTER
+#define SECCOMP_SET_MODE_FILTER 1
+#endif
+#ifndef SECCOMP_FILTER_FLAG_TSYNC
+#define SECCOMP_FILTER_FLAG_TSYNC 1
+#endif
+
+#ifndef SECCOMP_RET_KILL
+// Return values supported for BPF filter programs. Please note that the
+// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
+// ever be used internally, and would result in the kernel killing our process.
+#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
+#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
+#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
+#define SECCOMP_RET_DATA 0x0000ffffU // sections
+#else
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#endif
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif
+
+// Impose some reasonable maximum BPF program size. Realistically, the
+// kernel probably has much lower limits. But by limiting to less than
+// 30 bits, we can ease requirements on some of our data types.
+#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
+
+#if defined(__i386__)
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_I386
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+
+#if defined(__BIONIC__)
+// Old Bionic versions don't have sys/user.h, so we just define regs_struct
+// directly. This can be removed once we no longer need to support these old
+// Bionic versions.
+struct regs_struct {
+ long int ebx;
+ long int ecx;
+ long int edx;
+ long int esi;
+ long int edi;
+ long int ebp;
+ long int eax;
+ long int xds;
+ long int xes;
+ long int xfs;
+ long int xgs;
+ long int orig_eax;
+ long int eip;
+ long int xcs;
+ long int eflags;
+ long int esp;
+ long int xss;
+};
+#else
+typedef user_regs_struct regs_struct;
+#endif
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).eax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_eax
+#define SECCOMP_PT_IP(_regs) (_regs).eip
+#define SECCOMP_PT_PARM1(_regs) (_regs).ebx
+#define SECCOMP_PT_PARM2(_regs) (_regs).ecx
+#define SECCOMP_PT_PARM3(_regs) (_regs).edx
+#define SECCOMP_PT_PARM4(_regs) (_regs).esi
+#define SECCOMP_PT_PARM5(_regs) (_regs).edi
+#define SECCOMP_PT_PARM6(_regs) (_regs).ebp
+
+#elif defined(__x86_64__)
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_X86_64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+typedef user_regs_struct regs_struct;
+#define SECCOMP_PT_RESULT(_regs) (_regs).rax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_rax
+#define SECCOMP_PT_IP(_regs) (_regs).rip
+#define SECCOMP_PT_PARM1(_regs) (_regs).rdi
+#define SECCOMP_PT_PARM2(_regs) (_regs).rsi
+#define SECCOMP_PT_PARM3(_regs) (_regs).rdx
+#define SECCOMP_PT_PARM4(_regs) (_regs).r10
+#define SECCOMP_PT_PARM5(_regs) (_regs).r8
+#define SECCOMP_PT_PARM6(_regs) (_regs).r9
+
+#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
+// and a "ghost syscall private to the kernel", cmpxchg,
+// at |__ARM_NR_BASE+0x00fff0|.
+// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
+#define MIN_SYSCALL ((unsigned int)__NR_SYSCALL_BASE)
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u)
+#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE)
+#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
+#define MIN_GHOST_SYSCALL ((unsigned int)__ARM_NR_BASE + 0xfff0u)
+#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u)
+
+#define SECCOMP_ARCH AUDIT_ARCH_ARM
+
+// ARM sigcontext_t is different from i386/x86_64.
+// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
+// ARM EABI syscall convention.
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#if defined(__BIONIC__)
+// Old Bionic versions don't have sys/user.h, so we just define regs_struct
+// directly. This can be removed once we no longer need to support these old
+// Bionic versions.
+struct regs_struct {
+ unsigned long uregs[18];
+};
+#else
+typedef user_regs regs_struct;
+#endif
+
+#define REG_cpsr uregs[16]
+#define REG_pc uregs[15]
+#define REG_lr uregs[14]
+#define REG_sp uregs[13]
+#define REG_ip uregs[12]
+#define REG_fp uregs[11]
+#define REG_r10 uregs[10]
+#define REG_r9 uregs[9]
+#define REG_r8 uregs[8]
+#define REG_r7 uregs[7]
+#define REG_r6 uregs[6]
+#define REG_r5 uregs[5]
+#define REG_r4 uregs[4]
+#define REG_r3 uregs[3]
+#define REG_r2 uregs[2]
+#define REG_r1 uregs[1]
+#define REG_r0 uregs[0]
+#define REG_ORIG_r0 uregs[17]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_r0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_r7
+#define SECCOMP_PT_IP(_regs) (_regs).REG_pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_r0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_r1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_r2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_r3
+#define SECCOMP_PT_PARM5(_regs) (_regs).REG_r4
+#define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5
+
+#elif defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+#define MIN_SYSCALL __NR_O32_Linux
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_Linux_syscalls)
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_MIPSEL
+#define SYSCALL_EIGHT_ARGS
+// MIPS sigcontext_t is different from i386/x86_64 and ARM.
+// See </arch/mips/include/uapi/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[_reg])
+// Based on MIPS o32 ABI syscall convention.
+// On MIPS, when indirect syscall is being made (syscall(__NR_foo)),
+// real identificator (__NR_foo) is not in v0, but in a0
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 5)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 6)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 7)
+// Only the first 4 arguments of syscall are in registers.
+// The rest are on the stack.
+#define SECCOMP_STACKPARM(_ctx, n) (((long *)SECCOMP_REG(_ctx, 29))[(n)])
+#define SECCOMP_PARM5(_ctx) SECCOMP_STACKPARM(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_STACKPARM(_ctx, 5)
+#define SECCOMP_PARM7(_ctx) SECCOMP_STACKPARM(_ctx, 6)
+#define SECCOMP_PARM8(_ctx) SECCOMP_STACKPARM(_ctx, 7)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+// On Mips we don't have structures like user_regs or user_regs_struct in
+// sys/user.h that we could use, so we just define regs_struct directly.
+struct regs_struct {
+ unsigned long long regs[32];
+};
+
+#define REG_a3 regs[7]
+#define REG_a2 regs[6]
+#define REG_a1 regs[5]
+#define REG_a0 regs[4]
+#define REG_v1 regs[3]
+#define REG_v0 regs[2]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_v0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_v0
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_a0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_a1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_a2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_a3
+
+#elif defined(__aarch64__)
+struct regs_struct {
+ unsigned long long regs[31];
+ unsigned long long sp;
+ unsigned long long pc;
+ unsigned long long pstate;
+};
+
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 279u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_AARCH64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.regs[_reg])
+
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 8)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, 5)
+
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX \
+ (offsetof(struct arch_seccomp_data, instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX \
+ (offsetof(struct arch_seccomp_data, instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) \
+ (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) \
+ (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 0)
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).regs[0]
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).regs[8]
+#define SECCOMP_PT_IP(_regs) (_regs).pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).regs[0]
+#define SECCOMP_PT_PARM2(_regs) (_regs).regs[1]
+#define SECCOMP_PT_PARM3(_regs) (_regs).regs[2]
+#define SECCOMP_PT_PARM4(_regs) (_regs).regs[3]
+#define SECCOMP_PT_PARM5(_regs) (_regs).regs[4]
+#define SECCOMP_PT_PARM6(_regs) (_regs).regs[5]
+#else
+#error Unsupported target platform
+
+#endif
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
new file mode 100644
index 0000000..8a9b3f7
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -0,0 +1,525 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+// Some headers on Android are missing cdefs: crbug.com/172337.
+// (We can't use OS_ANDROID here since build_config.h is not included).
+#if defined(ANDROID)
+#include <sys/cdefs.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/filter.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/seccomp-bpf/verifier.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::SandboxBPFDSLPolicy;
+
+namespace sandbox {
+
+namespace {
+
+const int kExpectedExitCode = 100;
+
+#if !defined(NDEBUG)
+void WriteFailedStderrSetupMessage(int out_fd) {
+ const char* error_string = strerror(errno);
+ static const char msg[] =
+ "You have reproduced a puzzling issue.\n"
+ "Please, report to crbug.com/152530!\n"
+ "Failed to set up stderr: ";
+ if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string &&
+ HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
+ HANDLE_EINTR(write(out_fd, "\n", 1))) {
+ }
+}
+#endif // !defined(NDEBUG)
+
+// We define a really simple sandbox policy. It is just good enough for us
+// to tell that the sandbox has actually been activated.
+class ProbePolicy : public SandboxBPFDSLPolicy {
+ public:
+ ProbePolicy() {}
+ virtual ~ProbePolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysnum) const override {
+ switch (sysnum) {
+ case __NR_getpid:
+ // Return EPERM so that we can check that the filter actually ran.
+ return Error(EPERM);
+ case __NR_exit_group:
+ // Allow exit() with a non-default return code.
+ return Allow();
+ default:
+ // Make everything else fail in an easily recognizable way.
+ return Error(EINVAL);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProbePolicy);
+};
+
+void ProbeProcess(void) {
+ if (syscall(__NR_getpid) < 0 && errno == EPERM) {
+ syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
+ }
+}
+
+class AllowAllPolicy : public SandboxBPFDSLPolicy {
+ public:
+ AllowAllPolicy() {}
+ virtual ~AllowAllPolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysnum) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysnum));
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
+};
+
+void TryVsyscallProcess(void) {
+ time_t current_time;
+ // time() is implemented as a vsyscall. With an older glibc, with
+ // vsyscall=emulate and some versions of the seccomp BPF patch
+ // we may get SIGKILL-ed. Detect this!
+ if (time(¤t_time) != static_cast<time_t>(-1)) {
+ syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
+ }
+}
+
+bool IsSingleThreaded(int proc_fd) {
+ if (proc_fd < 0) {
+ // Cannot determine whether program is single-threaded. Hope for
+ // the best...
+ return true;
+ }
+
+ struct stat sb;
+ int task = -1;
+ if ((task = openat(proc_fd, "self/task", O_RDONLY | O_DIRECTORY)) < 0 ||
+ fstat(task, &sb) != 0 || sb.st_nlink != 3 || IGNORE_EINTR(close(task))) {
+ if (task >= 0) {
+ if (IGNORE_EINTR(close(task))) {
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+SandboxBPF::SandboxBPF()
+ : quiet_(false), proc_fd_(-1), sandbox_has_started_(false), policy_() {
+}
+
+SandboxBPF::~SandboxBPF() {
+}
+
+bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
+ return SyscallSet::IsValid(sysnum);
+}
+
+bool SandboxBPF::RunFunctionInPolicy(
+ void (*code_in_sandbox)(),
+ scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> policy) {
+ // Block all signals before forking a child process. This prevents an
+ // attacker from manipulating our test by sending us an unexpected signal.
+ sigset_t old_mask, new_mask;
+ if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
+ SANDBOX_DIE("sigprocmask() failed");
+ }
+ int fds[2];
+ if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) {
+ SANDBOX_DIE("pipe() failed");
+ }
+
+ if (fds[0] <= 2 || fds[1] <= 2) {
+ SANDBOX_DIE("Process started without standard file descriptors");
+ }
+
+ // This code is using fork() and should only ever run single-threaded.
+ // Most of the code below is "async-signal-safe" and only minor changes
+ // would be needed to support threads.
+ DCHECK(IsSingleThreaded(proc_fd_));
+ pid_t pid = fork();
+ if (pid < 0) {
+ // Die if we cannot fork(). We would probably fail a little later
+ // anyway, as the machine is likely very close to running out of
+ // memory.
+ // But what we don't want to do is return "false", as a crafty
+ // attacker might cause fork() to fail at will and could trick us
+ // into running without a sandbox.
+ sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails
+ SANDBOX_DIE("fork() failed unexpectedly");
+ }
+
+ // In the child process
+ if (!pid) {
+ // Test a very simple sandbox policy to verify that we can
+ // successfully turn on sandboxing.
+ Die::EnableSimpleExit();
+
+ errno = 0;
+ if (IGNORE_EINTR(close(fds[0]))) {
+ // This call to close() has been failing in strange ways. See
+ // crbug.com/152530. So we only fail in debug mode now.
+#if !defined(NDEBUG)
+ WriteFailedStderrSetupMessage(fds[1]);
+ SANDBOX_DIE(NULL);
+#endif
+ }
+ if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) {
+ // Stderr could very well be a file descriptor to .xsession-errors, or
+ // another file, which could be backed by a file system that could cause
+ // dup2 to fail while trying to close stderr. It's important that we do
+ // not fail on trying to close stderr.
+ // If dup2 fails here, we will continue normally, this means that our
+ // parent won't cause a fatal failure if something writes to stderr in
+ // this child.
+#if !defined(NDEBUG)
+ // In DEBUG builds, we still want to get a report.
+ WriteFailedStderrSetupMessage(fds[1]);
+ SANDBOX_DIE(NULL);
+#endif
+ }
+ if (IGNORE_EINTR(close(fds[1]))) {
+ // This call to close() has been failing in strange ways. See
+ // crbug.com/152530. So we only fail in debug mode now.
+#if !defined(NDEBUG)
+ WriteFailedStderrSetupMessage(fds[1]);
+ SANDBOX_DIE(NULL);
+#endif
+ }
+
+ SetSandboxPolicy(policy.release());
+ if (!StartSandbox(PROCESS_SINGLE_THREADED)) {
+ SANDBOX_DIE(NULL);
+ }
+
+ // Run our code in the sandbox.
+ code_in_sandbox();
+
+ // code_in_sandbox() is not supposed to return here.
+ SANDBOX_DIE(NULL);
+ }
+
+ // In the parent process.
+ if (IGNORE_EINTR(close(fds[1]))) {
+ SANDBOX_DIE("close() failed");
+ }
+ if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) {
+ SANDBOX_DIE("sigprocmask() failed");
+ }
+ int status;
+ if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) {
+ SANDBOX_DIE("waitpid() failed unexpectedly");
+ }
+ bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode;
+
+ // If we fail to support sandboxing, there might be an additional
+ // error message. If so, this was an entirely unexpected and fatal
+ // failure. We should report the failure and somebody must fix
+ // things. This is probably a security-critical bug in the sandboxing
+ // code.
+ if (!rc) {
+ char buf[4096];
+ ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1));
+ if (len > 0) {
+ while (len > 1 && buf[len - 1] == '\n') {
+ --len;
+ }
+ buf[len] = '\000';
+ SANDBOX_DIE(buf);
+ }
+ }
+ if (IGNORE_EINTR(close(fds[0]))) {
+ SANDBOX_DIE("close() failed");
+ }
+
+ return rc;
+}
+
+bool SandboxBPF::KernelSupportSeccompBPF() {
+ return RunFunctionInPolicy(
+ ProbeProcess,
+ scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy>(new ProbePolicy())) &&
+ RunFunctionInPolicy(
+ TryVsyscallProcess,
+ scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy>(new AllowAllPolicy()));
+}
+
+// static
+SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) {
+ // It the sandbox is currently active, we clearly must have support for
+ // sandboxing.
+ if (status_ == STATUS_ENABLED) {
+ return status_;
+ }
+
+ // Even if the sandbox was previously available, something might have
+ // changed in our run-time environment. Check one more time.
+ if (status_ == STATUS_AVAILABLE) {
+ if (!IsSingleThreaded(proc_fd)) {
+ status_ = STATUS_UNAVAILABLE;
+ }
+ return status_;
+ }
+
+ if (status_ == STATUS_UNAVAILABLE && IsSingleThreaded(proc_fd)) {
+ // All state transitions resulting in STATUS_UNAVAILABLE are immediately
+ // preceded by STATUS_AVAILABLE. Furthermore, these transitions all
+ // happen, if and only if they are triggered by the process being multi-
+ // threaded.
+ // In other words, if a single-threaded process is currently in the
+ // STATUS_UNAVAILABLE state, it is safe to assume that sandboxing is
+ // actually available.
+ status_ = STATUS_AVAILABLE;
+ return status_;
+ }
+
+ // If we have not previously checked for availability of the sandbox or if
+ // we otherwise don't believe to have a good cached value, we have to
+ // perform a thorough check now.
+ if (status_ == STATUS_UNKNOWN) {
+ // We create our own private copy of a "Sandbox" object. This ensures that
+ // the object does not have any policies configured, that might interfere
+ // with the tests done by "KernelSupportSeccompBPF()".
+ SandboxBPF sandbox;
+
+ // By setting "quiet_ = true" we suppress messages for expected and benign
+ // failures (e.g. if the current kernel lacks support for BPF filters).
+ sandbox.quiet_ = true;
+ sandbox.set_proc_fd(proc_fd);
+ status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE
+ : STATUS_UNSUPPORTED;
+
+ // As we are performing our tests from a child process, the run-time
+ // environment that is visible to the sandbox is always guaranteed to be
+ // single-threaded. Let's check here whether the caller is single-
+ // threaded. Otherwise, we mark the sandbox as temporarily unavailable.
+ if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) {
+ status_ = STATUS_UNAVAILABLE;
+ }
+ }
+ return status_;
+}
+
+// static
+SandboxBPF::SandboxStatus
+SandboxBPF::SupportsSeccompThreadFilterSynchronization() {
+ // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across
+ // the thread group are all handled atomically by this syscall.
+ const int rv = syscall(
+ __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL);
+
+ if (rv == -1 && errno == EFAULT) {
+ return STATUS_AVAILABLE;
+ } else {
+ // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
+ CHECK_EQ(-1, rv);
+ CHECK(ENOSYS == errno || EINVAL == errno);
+ return STATUS_UNSUPPORTED;
+ }
+}
+
+void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; }
+
+bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) {
+ CHECK(thread_state == PROCESS_SINGLE_THREADED ||
+ thread_state == PROCESS_MULTI_THREADED);
+
+ if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
+ SANDBOX_DIE(
+ "Trying to start sandbox, even though it is known to be "
+ "unavailable");
+ return false;
+ } else if (sandbox_has_started_) {
+ SANDBOX_DIE(
+ "Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
+ return false;
+ }
+ if (proc_fd_ < 0) {
+ proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY);
+ }
+ if (proc_fd_ < 0) {
+ // For now, continue in degraded mode, if we can't access /proc.
+ // In the future, we might want to tighten this requirement.
+ }
+
+ bool supports_tsync =
+ SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE;
+
+ if (thread_state == PROCESS_SINGLE_THREADED) {
+ if (!IsSingleThreaded(proc_fd_)) {
+ SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded");
+ return false;
+ }
+ } else if (thread_state == PROCESS_MULTI_THREADED) {
+ if (IsSingleThreaded(proc_fd_)) {
+ SANDBOX_DIE("Cannot start sandbox; "
+ "process may be single-threaded when reported as not");
+ return false;
+ }
+ if (!supports_tsync) {
+ SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
+ "filters for a threadgroup");
+ return false;
+ }
+ }
+
+ // We no longer need access to any files in /proc. We want to do this
+ // before installing the filters, just in case that our policy denies
+ // close().
+ if (proc_fd_ >= 0) {
+ if (IGNORE_EINTR(close(proc_fd_))) {
+ SANDBOX_DIE("Failed to close file descriptor for /proc");
+ return false;
+ }
+ proc_fd_ = -1;
+ }
+
+ // Install the filters.
+ InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED);
+
+ // We are now inside the sandbox.
+ status_ = STATUS_ENABLED;
+
+ return true;
+}
+
+// Don't take a scoped_ptr here, polymorphism make their use awkward.
+void SandboxBPF::SetSandboxPolicy(bpf_dsl::SandboxBPFDSLPolicy* policy) {
+ DCHECK(!policy_);
+ if (sandbox_has_started_) {
+ SANDBOX_DIE("Cannot change policy after sandbox has started");
+ }
+ policy_.reset(policy);
+}
+
+void SandboxBPF::InstallFilter(bool must_sync_threads) {
+ // We want to be very careful in not imposing any requirements on the
+ // policies that are set with SetSandboxPolicy(). This means, as soon as
+ // the sandbox is active, we shouldn't be relying on libraries that could
+ // be making system calls. This, for example, means we should avoid
+ // using the heap and we should avoid using STL functions.
+ // Temporarily copy the contents of the "program" vector into a
+ // stack-allocated array; and then explicitly destroy that object.
+ // This makes sure we don't ex- or implicitly call new/delete after we
+ // installed the BPF filter program in the kernel. Depending on the
+ // system memory allocator that is in effect, these operators can result
+ // in system calls to things like munmap() or brk().
+ CodeGen::Program* program = AssembleFilter(false).release();
+
+ struct sock_filter bpf[program->size()];
+ const struct sock_fprog prog = {static_cast<unsigned short>(program->size()),
+ bpf};
+ memcpy(bpf, &(*program)[0], sizeof(bpf));
+ delete program;
+
+ // Make an attempt to release memory that is no longer needed here, rather
+ // than in the destructor. Try to avoid as much as possible to presume of
+ // what will be possible to do in the new (sandboxed) execution environment.
+ policy_.reset();
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs");
+ }
+
+ // Install BPF filter program. If the thread state indicates multi-threading
+ // support, then the kernel hass the seccomp system call. Otherwise, fall
+ // back on prctl, which requires the process to be single-threaded.
+ if (must_sync_threads) {
+ int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, reinterpret_cast<const char*>(&prog));
+ if (rv) {
+ SANDBOX_DIE(quiet_ ? NULL :
+ "Kernel refuses to turn on and synchronize threads for BPF filters");
+ }
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
+ }
+ }
+
+ sandbox_has_started_ = true;
+}
+
+scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter(
+ bool force_verification) {
+#if !defined(NDEBUG)
+ force_verification = true;
+#endif
+
+ bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry());
+ scoped_ptr<CodeGen::Program> program = compiler.Compile();
+
+ // Make sure compilation resulted in BPF program that executes
+ // correctly. Otherwise, there is an internal error in our BPF compiler.
+ // There is really nothing the caller can do until the bug is fixed.
+ if (force_verification) {
+ // Verification is expensive. We only perform this step, if we are
+ // compiled in debug mode, or if the caller explicitly requested
+ // verification.
+
+ const char* err = NULL;
+ if (!Verifier::VerifyBPF(&compiler, *program, *policy_, &err)) {
+ CodeGen::PrintProgram(*program);
+ SANDBOX_DIE(err);
+ }
+ }
+
+ return program.Pass();
+}
+
+bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) {
+ return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno);
+}
+
+intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
+ return Syscall::Call(args.nr,
+ static_cast<intptr_t>(args.args[0]),
+ static_cast<intptr_t>(args.args[1]),
+ static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]),
+ static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
+}
+
+SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
new file mode 100644
index 0000000..866e764
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+struct arch_seccomp_data;
+namespace bpf_dsl {
+class SandboxBPFDSLPolicy;
+}
+
+class SANDBOX_EXPORT SandboxBPF {
+ public:
+ enum SandboxStatus {
+ STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox()
+ STATUS_UNSUPPORTED, // The kernel does not appear to support sandboxing
+ STATUS_UNAVAILABLE, // Currently unavailable but might work again later
+ STATUS_AVAILABLE, // Sandboxing is available but not currently active
+ STATUS_ENABLED // The sandbox is now active
+ };
+
+ // Depending on the level of kernel support, seccomp-bpf may require the
+ // process to be single-threaded in order to enable it. When calling
+ // StartSandbox(), the program should indicate whether or not the sandbox
+ // should try and engage with multi-thread support.
+ enum SandboxThreadState {
+ PROCESS_INVALID,
+ PROCESS_SINGLE_THREADED, // The program is currently single-threaded.
+ // Note: PROCESS_MULTI_THREADED requires experimental kernel support that
+ // has not been contributed to upstream Linux.
+ PROCESS_MULTI_THREADED, // The program may be multi-threaded.
+ };
+
+ // Constructors and destructors.
+ // NOTE: Setting a policy and starting the sandbox is a one-way operation.
+ // The kernel does not provide any option for unloading a loaded
+ // sandbox. Strictly speaking, that means we should disallow calling
+ // the destructor, if StartSandbox() has ever been called. In practice,
+ // this makes it needlessly complicated to operate on "Sandbox"
+ // objects. So, we instead opted to allow object destruction. But it
+ // should be noted that during its lifetime, the object probably made
+ // irreversible state changes to the runtime environment. These changes
+ // stay in effect even after the destructor has been run.
+ SandboxBPF();
+ ~SandboxBPF();
+
+ // Checks whether a particular system call number is valid on the current
+ // architecture. E.g. on ARM there's a non-contiguous range of private
+ // system calls.
+ static bool IsValidSyscallNumber(int sysnum);
+
+ // There are a lot of reasons why the Seccomp sandbox might not be available.
+ // This could be because the kernel does not support Seccomp mode, or it
+ // could be because another sandbox is already active.
+ // "proc_fd" should be a file descriptor for "/proc", or -1 if not
+ // provided by the caller.
+ static SandboxStatus SupportsSeccompSandbox(int proc_fd);
+
+ // Determines if the kernel has support for the seccomp() system call to
+ // synchronize BPF filters across a thread group.
+ static SandboxStatus SupportsSeccompThreadFilterSynchronization();
+
+ // The sandbox needs to be able to access files in "/proc/self". If this
+ // directory is not accessible when "startSandbox()" gets called, the caller
+ // can provide an already opened file descriptor by calling "set_proc_fd()".
+ // The sandbox becomes the new owner of this file descriptor and will
+ // eventually close it when "StartSandbox()" executes.
+ void set_proc_fd(int proc_fd);
+
+ // Set the BPF policy as |policy|. Ownership of |policy| is transfered here
+ // to the sandbox object.
+ void SetSandboxPolicy(bpf_dsl::SandboxBPFDSLPolicy* policy);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // From within an UnsafeTrap() it is often useful to be able to execute
+ // the system call that triggered the trap. The ForwardSyscall() method
+ // makes this easy. It is more efficient than calling glibc's syscall()
+ // function, as it avoid the extra round-trip to the signal handler. And
+ // it automatically does the correct thing to report kernel-style error
+ // conditions, rather than setting errno. See the comments for TrapFnc for
+ // details. In other words, the return value from ForwardSyscall() is
+ // directly suitable as a return value for a trap handler.
+ static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
+
+ // This is the main public entry point. It finds all system calls that
+ // need rewriting, sets up the resources needed by the sandbox, and
+ // enters Seccomp mode.
+ // The calling process must specify its current SandboxThreadState, as a way
+ // to tell the sandbox which type of kernel support it should engage.
+ // It is possible to stack multiple sandboxes by creating separate "Sandbox"
+ // objects and calling "StartSandbox()" on each of them. Please note, that
+ // this requires special care, though, as newly stacked sandboxes can never
+ // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+ // a new policy requires making system calls, that might already be
+ // disallowed.
+ // Finally, stacking does add more kernel overhead than having a single
+ // combined policy. So, it should only be used if there are no alternatives.
+ bool StartSandbox(SandboxThreadState thread_state) WARN_UNUSED_RESULT;
+
+ // Assembles a BPF filter program from the current policy. After calling this
+ // function, you must not call any other sandboxing function.
+ // Typically, AssembleFilter() is only used by unit tests and by sandbox
+ // internals. It should not be used by production code.
+ // For performance reasons, we normally only run the assembled BPF program
+ // through the verifier, iff the program was built in debug mode.
+ // But by setting "force_verification", the caller can request that the
+ // verifier is run unconditionally. This is useful for unittests.
+ scoped_ptr<CodeGen::Program> AssembleFilter(bool force_verification);
+
+ private:
+ // Get a file descriptor pointing to "/proc", if currently available.
+ int proc_fd() { return proc_fd_; }
+
+ // Creates a subprocess and runs "code_in_sandbox" inside of the specified
+ // policy. The caller has to make sure that "this" has not yet been
+ // initialized with any other policies.
+ bool RunFunctionInPolicy(void (*code_in_sandbox)(),
+ scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> policy);
+
+ // Performs a couple of sanity checks to verify that the kernel supports the
+ // features that we need for successful sandboxing.
+ // The caller has to make sure that "this" has not yet been initialized with
+ // any other policies.
+ bool KernelSupportSeccompBPF();
+
+ // Assembles and installs a filter based on the policy that has previously
+ // been configured with SetSandboxPolicy().
+ void InstallFilter(bool must_sync_threads);
+
+ // Verify the correctness of a compiled program by comparing it against the
+ // current policy. This function should only ever be called by unit tests and
+ // by the sandbox internals. It should not be used by production code.
+ void VerifyProgram(const CodeGen::Program& program);
+
+ static SandboxStatus status_;
+
+ bool quiet_;
+ int proc_fd_;
+ bool sandbox_has_started_;
+ scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
new file mode 100644
index 0000000..19d3e8a
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+#include <fcntl.h>
+#include <linux/filter.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+SandboxBPFTestRunner::SandboxBPFTestRunner(
+ BPFTesterDelegate* bpf_tester_delegate)
+ : bpf_tester_delegate_(bpf_tester_delegate) {
+}
+
+SandboxBPFTestRunner::~SandboxBPFTestRunner() {
+}
+
+void SandboxBPFTestRunner::Run() {
+ DCHECK(bpf_tester_delegate_);
+ sandbox::Die::EnableSimpleExit();
+
+ scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> policy =
+ bpf_tester_delegate_->GetSandboxBPFPolicy();
+
+ if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) ==
+ sandbox::SandboxBPF::STATUS_AVAILABLE) {
+ // Ensure the the sandbox is actually available at this time
+ int proc_fd;
+ SANDBOX_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0);
+ SANDBOX_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) ==
+ sandbox::SandboxBPF::STATUS_AVAILABLE);
+
+ // Initialize and then start the sandbox with our custom policy
+ sandbox::SandboxBPF sandbox;
+ sandbox.set_proc_fd(proc_fd);
+ sandbox.SetSandboxPolicy(policy.release());
+ SANDBOX_ASSERT(
+ sandbox.StartSandbox(sandbox::SandboxBPF::PROCESS_SINGLE_THREADED));
+
+ // Run the actual test.
+ bpf_tester_delegate_->RunTestFunction();
+ } else {
+ printf("This BPF test is not fully running in this configuration!\n");
+ // Android and Valgrind are the only configurations where we accept not
+ // having kernel BPF support.
+ if (!IsAndroid() && !IsRunningOnValgrind()) {
+ const bool seccomp_bpf_is_supported = false;
+ SANDBOX_ASSERT(seccomp_bpf_is_supported);
+ }
+ // Call the compiler and verify the policy. That's the least we can do,
+ // if we don't have kernel support.
+ sandbox::SandboxBPF sandbox;
+ sandbox.SetSandboxPolicy(policy.release());
+ sandbox.AssembleFilter(true /* force_verification */);
+ sandbox::UnitTests::IgnoreThisTest();
+ }
+}
+
+bool SandboxBPFTestRunner::ShouldCheckForLeaks() const {
+ // LSAN requires being able to use ptrace() and other system calls that could
+ // be denied.
+ return false;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
new file mode 100644
index 0000000..626ac4e
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+// To create a SandboxBPFTestRunner object, one needs to implement this
+// interface and pass an instance to the SandboxBPFTestRunner constructor.
+// In the child process running the test, the BPFTesterDelegate object is
+// guaranteed to not be destroyed until the child process terminates.
+class BPFTesterDelegate {
+ public:
+ BPFTesterDelegate() {}
+ virtual ~BPFTesterDelegate() {}
+
+ // This will instanciate a policy suitable for the test we want to run. It is
+ // guaranteed to only be called from the child process that will run the
+ // test.
+ virtual scoped_ptr<bpf_dsl::SandboxBPFDSLPolicy> GetSandboxBPFPolicy() = 0;
+ // This will be called from a child process with the BPF sandbox turned on.
+ virtual void RunTestFunction() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterDelegate);
+};
+
+// This class implements the SandboxTestRunner interface and Run() will
+// initialize a seccomp-bpf sandbox (specified by |bpf_tester_delegate|) and
+// run a test function (via |bpf_tester_delegate|) if the current kernel
+// configuration allows it. If it can not run the test under seccomp-bpf,
+// Run() will still compile the policy which should allow to get some coverage
+// under tools such as Valgrind.
+class SandboxBPFTestRunner : public SandboxTestRunner {
+ public:
+ // This constructor takes ownership of the |bpf_tester_delegate| object.
+ // (It doesn't take a scoped_ptr since they make polymorphism verbose).
+ explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate);
+ virtual ~SandboxBPFTestRunner();
+
+ virtual void Run() override;
+
+ virtual bool ShouldCheckForLeaks() const override;
+
+ private:
+ scoped_ptr<BPFTesterDelegate> bpf_tester_delegate_;
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPFTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
new file mode 100644
index 0000000..57f6baf
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -0,0 +1,413 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#include <asm/unistd.h>
+#include <errno.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY)
+// Number that's not currently used by any Linux kernel ABIs.
+const int kInvalidSyscallNumber = 0x351d3;
+#else
+#error Unrecognized architecture
+#endif
+
+asm(// We need to be able to tell the kernel exactly where we made a
+ // system call. The C++ compiler likes to sometimes clone or
+ // inline code, which would inadvertently end up duplicating
+ // the entry point.
+ // "gcc" can suppress code duplication with suitable function
+ // attributes, but "clang" doesn't have this ability.
+ // The "clang" developer mailing list suggested that the correct
+ // and portable solution is a file-scope assembly block.
+ // N.B. We do mark our code as a proper function so that backtraces
+ // work correctly. But we make absolutely no attempt to use the
+ // ABI's calling conventions for passing arguments. We will only
+ // ever be called from assembly code and thus can pick more
+ // suitable calling conventions.
+#if defined(__i386__)
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%eax" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "int $0x80". This address can be
+ // used as a marker that BPF code inspects.
+ "test %eax, %eax\n"
+ "jge 1f\n"
+ // Always, make sure that our code is position-independent, or
+ // address space randomization might not work on i386. This means,
+ // we can't use "lea", but instead have to rely on "call/pop".
+ "call 0f; .cfi_adjust_cfa_offset 4\n"
+ "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
+ "addl $2f-0b, %eax\n"
+ "ret\n"
+ // Save register that we don't want to clobber. On i386, we need to
+ // save relatively aggressively, as there are a couple or registers
+ // that are used internally (e.g. %ebx for position-independent
+ // code, and %ebp for the frame pointer), and as we need to keep at
+ // least a few registers available for the register allocator.
+ "1:push %esi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset esi, 0\n"
+ "push %edi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset edi, 0\n"
+ "push %ebx; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebx, 0\n"
+ "push %ebp; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebp, 0\n"
+ // Copy entries from the array holding the arguments into the
+ // correct CPU registers.
+ "movl 0(%edi), %ebx\n"
+ "movl 4(%edi), %ecx\n"
+ "movl 8(%edi), %edx\n"
+ "movl 12(%edi), %esi\n"
+ "movl 20(%edi), %ebp\n"
+ "movl 16(%edi), %edi\n"
+ // Enter the kernel.
+ "int $0x80\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:"
+ // Restore any clobbered registers that we didn't declare to the
+ // compiler.
+ "pop %ebp; .cfi_restore ebp; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebx; .cfi_restore ebx; .cfi_adjust_cfa_offset -4\n"
+ "pop %edi; .cfi_restore edi; .cfi_adjust_cfa_offset -4\n"
+ "pop %esi; .cfi_restore esi; .cfi_adjust_cfa_offset -4\n"
+ "ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__x86_64__)
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%rdi" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "test %rdi, %rdi\n"
+ "jge 1f\n"
+ // Always make sure that our code is position-independent, or the
+ // linker will throw a hissy fit on x86-64.
+ "lea 2f(%rip), %rax\n"
+ "ret\n"
+ // Now we load the registers used to pass arguments to the system
+ // call: system call number in %rax, and arguments in %rdi, %rsi,
+ // %rdx, %r10, %r8, %r9. Note: These are all caller-save registers
+ // (only %rbx, %rbp, %rsp, and %r12-%r15 are callee-save), so no
+ // need to worry here about spilling registers or CFI directives.
+ "1:movq %rdi, %rax\n"
+ "movq 0(%rsi), %rdi\n"
+ "movq 16(%rsi), %rdx\n"
+ "movq 24(%rsi), %r10\n"
+ "movq 32(%rsi), %r8\n"
+ "movq 40(%rsi), %r9\n"
+ "movq 8(%rsi), %rsi\n"
+ // Enter the kernel.
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__arm__)
+ // Throughout this file, we use the same mode (ARM vs. thumb)
+ // that the C++ compiler uses. This means, when transfering control
+ // from C++ to assembly code, we do not need to switch modes (e.g.
+ // by using the "bx" instruction). It also means that our assembly
+ // code should not be invoked directly from code that lives in
+ // other compilation units, as we don't bother implementing thumb
+ // interworking. That's OK, as we don't make any of the assembly
+ // symbols public. They are all local to this file.
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+#if defined(__thumb__)
+ ".thumb_func\n"
+#else
+ ".arm\n"
+#endif
+ "SyscallAsm:.fnstart\n"
+ "@ args = 0, pretend = 0, frame = 8\n"
+ "@ frame_needed = 1, uses_anonymous_args = 0\n"
+#if defined(__thumb__)
+ ".cfi_startproc\n"
+ "push {r7, lr}\n"
+ ".cfi_offset 14, -4\n"
+ ".cfi_offset 7, -8\n"
+ "mov r7, sp\n"
+ ".cfi_def_cfa_register 7\n"
+ ".cfi_def_cfa_offset 8\n"
+#else
+ "stmfd sp!, {fp, lr}\n"
+ "add fp, sp, #4\n"
+#endif
+ // Check if "r0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "swi 0". This address can be
+ // used as a marker that BPF code inspects.
+ "cmp r0, #0\n"
+ "bge 1f\n"
+ "adr r0, 2f\n"
+ "b 2f\n"
+ // We declared (almost) all clobbered registers to the compiler. On
+ // ARM there is no particular register pressure. So, we can go
+ // ahead and directly copy the entries from the arguments array
+ // into the appropriate CPU registers.
+ "1:ldr r5, [r6, #20]\n"
+ "ldr r4, [r6, #16]\n"
+ "ldr r3, [r6, #12]\n"
+ "ldr r2, [r6, #8]\n"
+ "ldr r1, [r6, #4]\n"
+ "mov r7, r0\n"
+ "ldr r0, [r6, #0]\n"
+ // Enter the kernel
+ "swi 0\n"
+// Restore the frame pointer. Also restore the program counter from
+// the link register; this makes us return to the caller.
+#if defined(__thumb__)
+ "2:pop {r7, pc}\n"
+ ".cfi_endproc\n"
+#else
+ "2:ldmfd sp!, {fp, pc}\n"
+#endif
+ ".fnend\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__mips__)
+ ".text\n"
+ ".align 4\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.ent SyscallAsm\n"
+ ".frame $sp, 40, $ra\n"
+ ".set push\n"
+ ".set noreorder\n"
+ "addiu $sp, $sp, -40\n"
+ "sw $ra, 36($sp)\n"
+ // Check if "v0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "bgez $v0, 1f\n"
+ " nop\n"
+ "la $v0, 2f\n"
+ "b 2f\n"
+ " nop\n"
+ // On MIPS first four arguments go to registers a0 - a3 and any
+ // argument after that goes to stack. We can go ahead and directly
+ // copy the entries from the arguments array into the appropriate
+ // CPU registers and on the stack.
+ "1:lw $a3, 28($a0)\n"
+ "lw $a2, 24($a0)\n"
+ "lw $a1, 20($a0)\n"
+ "lw $t0, 16($a0)\n"
+ "sw $a3, 28($sp)\n"
+ "sw $a2, 24($sp)\n"
+ "sw $a1, 20($sp)\n"
+ "sw $t0, 16($sp)\n"
+ "lw $a3, 12($a0)\n"
+ "lw $a2, 8($a0)\n"
+ "lw $a1, 4($a0)\n"
+ "lw $a0, 0($a0)\n"
+ // Enter the kernel
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ // Restore the return address from the stack.
+ "2:lw $ra, 36($sp)\n"
+ "jr $ra\n"
+ " addiu $sp, $sp, 40\n"
+ ".set pop\n"
+ ".end SyscallAsm\n"
+ ".size SyscallAsm,.-SyscallAsm\n"
+#elif defined(__aarch64__)
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+ "SyscallAsm:\n"
+ ".cfi_startproc\n"
+ "cmp x0, #0\n"
+ "b.ge 1f\n"
+ "adr x0,2f\n"
+ "b 2f\n"
+ "1:ldr x5, [x6, #40]\n"
+ "ldr x4, [x6, #32]\n"
+ "ldr x3, [x6, #24]\n"
+ "ldr x2, [x6, #16]\n"
+ "ldr x1, [x6, #8]\n"
+ "mov x8, x0\n"
+ "ldr x0, [x6, #0]\n"
+ // Enter the kernel
+ "svc 0\n"
+ "2:ret\n"
+ ".cfi_endproc\n"
+ ".size SyscallAsm, .-SyscallAsm\n"
+#endif
+ ); // asm
+
+#if defined(__x86_64__)
+extern "C" {
+intptr_t SyscallAsm(intptr_t nr, const intptr_t args[6]);
+}
+#endif
+
+} // namespace
+
+intptr_t Syscall::InvalidCall() {
+ // Explicitly pass eight zero arguments just in case.
+ return Call(kInvalidSyscallNumber, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+intptr_t Syscall::Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7) {
+ // We rely on "intptr_t" to be the exact size as a "void *". This is
+ // typically true, but just in case, we add a check. The language
+ // specification allows platforms some leeway in cases, where
+ // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect
+ // that this would only be an issue for IA64, which we are currently not
+ // planning on supporting. And it is even possible that this would work
+ // on IA64, but for lack of actual hardware, I cannot test.
+ COMPILE_ASSERT(sizeof(void*) == sizeof(intptr_t),
+ pointer_types_and_intptr_must_be_exactly_the_same_size);
+
+ // TODO(nedeljko): Enable use of more than six parameters on architectures
+ // where that makes sense.
+#if defined(__mips__)
+ const intptr_t args[8] = {p0, p1, p2, p3, p4, p5, p6, p7};
+#else
+ DCHECK_EQ(p6, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ DCHECK_EQ(p7, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
+#endif // defined(__mips__)
+
+// Invoke our file-scope assembly code. The constraints have been picked
+// carefully to match what the rest of the assembly code expects in input,
+// output, and clobbered registers.
+#if defined(__i386__)
+ intptr_t ret = nr;
+ asm volatile(
+ "call SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=a"(ret)
+ : "0"(ret), "D"(args)
+ : "cc", "esp", "memory", "ecx", "edx");
+#elif defined(__x86_64__)
+ intptr_t ret = SyscallAsm(nr, args);
+#elif defined(__arm__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("r0") = nr;
+ register const intptr_t* data __asm__("r6") = args;
+ asm volatile(
+ "bl SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "cc",
+ "lr",
+ "memory",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5"
+#if !defined(__thumb__)
+ // In thumb mode, we cannot use "r7" as a general purpose register, as
+ // it is our frame pointer. We have to manually manage and preserve
+ // it.
+ // In ARM mode, we have a dedicated frame pointer register and "r7" is
+ // thus available as a general purpose register. We don't preserve it,
+ // but instead mark it as clobbered.
+ ,
+ "r7"
+#endif // !defined(__thumb__)
+ );
+ ret = inout;
+ }
+#elif defined(__mips__)
+ int err_status;
+ intptr_t ret = Syscall::SandboxSyscallRaw(nr, args, &err_status);
+
+ if (err_status) {
+ // On error, MIPS returns errno from syscall instead of -errno.
+ // The purpose of this negation is for SandboxSyscall() to behave
+ // more like it would on other architectures.
+ ret = -ret;
+ }
+#elif defined(__aarch64__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("x0") = nr;
+ register const intptr_t* data __asm__("x6") = args;
+ asm volatile("bl SyscallAsm\n"
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "memory", "x1", "x2", "x3", "x4", "x5", "x8", "x30");
+ ret = inout;
+ }
+
+#else
+#error "Unimplemented architecture"
+#endif
+ return ret;
+}
+
+void Syscall::PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx) {
+#if defined(__mips__)
+ // Mips ABI states that on error a3 CPU register has non zero value and if
+ // there is no error, it should be zero.
+ if (ret_val <= -1 && ret_val >= -4095) {
+ // |ret_val| followes the Syscall::Call() convention of being -errno on
+ // errors. In order to write correct value to return register this sign
+ // needs to be changed back.
+ ret_val = -ret_val;
+ SECCOMP_PARM4(ctx) = 1;
+ } else
+ SECCOMP_PARM4(ctx) = 0;
+#endif
+ SECCOMP_RESULT(ctx) = static_cast<greg_t>(ret_val);
+}
+
+#if defined(__mips__)
+intptr_t Syscall::SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_ret) {
+ register intptr_t ret __asm__("v0") = nr;
+ // a3 register becomes non zero on error.
+ register intptr_t err_stat __asm__("a3") = 0;
+ {
+ register const intptr_t* data __asm__("a0") = args;
+ asm volatile(
+ "la $t9, SyscallAsm\n"
+ "jalr $t9\n"
+ " nop\n"
+ : "=r"(ret), "=r"(err_stat)
+ : "0"(ret),
+ "r"(data)
+ // a2 is in the clober list so inline assembly can not change its
+ // value.
+ : "memory", "ra", "t9", "a2");
+ }
+
+ // Set an error status so it can be used outside of this function
+ *err_ret = err_stat;
+
+ return ret;
+}
+#endif // defined(__mips__)
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall.h b/sandbox/linux/seccomp-bpf/syscall.h
new file mode 100644
index 0000000..3686df5
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+
+#include <signal.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+// Android's signal.h doesn't define ucontext etc.
+#if defined(OS_ANDROID)
+#include "sandbox/linux/services/android_ucontext.h"
+#endif
+
+namespace sandbox {
+
+// This purely static class can be used to perform system calls with some
+// low-level control.
+class SANDBOX_EXPORT Syscall {
+ public:
+ // InvalidCall() invokes Call() with a platform-appropriate syscall
+ // number that is guaranteed to not be implemented (i.e., normally
+ // returns -ENOSYS).
+ // This is primarily meant to be useful for writing sandbox policy
+ // unit tests.
+ static intptr_t InvalidCall();
+
+ // System calls can take up to six parameters (up to eight on some
+ // architectures). Traditionally, glibc
+ // implements this property by using variadic argument lists. This works, but
+ // confuses modern tools such as valgrind, because we are nominally passing
+ // uninitialized data whenever we call through this function and pass less
+ // than the full six arguments.
+ // So, instead, we use C++'s template system to achieve a very similar
+ // effect. C++ automatically sets the unused parameters to zero for us, and
+ // it also does the correct type expansion (e.g. from 32bit to 64bit) where
+ // necessary.
+ // We have to use C-style cast operators as we want to be able to accept both
+ // integer and pointer types.
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6,
+ class T7>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ (intptr_t)p7);
+ }
+
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ 0,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
+ return Call(nr, p0, p1, p2, p3, p4, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2, class T3>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
+ return Call(nr, p0, p1, p2, p3, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2) {
+ return Call(nr, p0, p1, p2, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1) {
+ return Call(nr, p0, p1, 0, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0>
+ static inline intptr_t Call(int nr, T0 p0) {
+ return Call(nr, p0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ static inline intptr_t Call(int nr) {
+ return Call(nr, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ // Set the registers in |ctx| to match what they would be after a system call
+ // returning |ret_val|. |ret_val| must follow the Syscall::Call() convention
+ // of being -errno on errors.
+ static void PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx);
+
+ private:
+ // This performs system call |nr| with the arguments p0 to p7 from a constant
+ // userland address, which is for instance observable by seccomp-bpf filters.
+ // The constant userland address from which these system calls are made will
+ // be returned if |nr| is passed as -1.
+ // On error, this function will return a value between -1 and -4095 which
+ // should be interpreted as -errno.
+ static intptr_t Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7);
+
+#if defined(__mips__)
+ // This function basically does on MIPS what SandboxSyscall() is doing on
+ // other architectures. However, because of specificity of MIPS regarding
+ // handling syscall errors, SandboxSyscall() is made as a wrapper for this
+ // function in order for SandboxSyscall() to behave more like on other
+ // architectures on places where return value from SandboxSyscall() is used
+ // directly (like in most tests).
+ // The syscall "nr" is called with arguments that are set in an array on which
+ // pointer "args" points to and an information weather there is an error or no
+ // is returned to SandboxSyscall() by err_stat.
+ static intptr_t SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_stat);
+#endif // defined(__mips__)
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Syscall);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
new file mode 100644
index 0000000..1b4e067
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+// This is true for Mips O32 ABI.
+COMPILE_ASSERT(MIN_SYSCALL == __NR_Linux, min_syscall_should_be_4000);
+#else
+// This true for supported architectures (Intel and ARM EABI).
+COMPILE_ASSERT(MIN_SYSCALL == 0u, min_syscall_should_always_be_zero);
+#endif
+
+// SyscallRange represents an inclusive range of system call numbers.
+struct SyscallRange {
+ uint32_t first;
+ uint32_t last;
+};
+
+const SyscallRange kValidSyscallRanges[] = {
+ // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
+ // on Intel architectures, but leaves room for private syscalls on ARM.
+ {MIN_SYSCALL, MAX_PUBLIC_SYSCALL},
+#if defined(__arm__)
+ // ARM EABI includes "ARM private" system calls starting at
+ // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
+ // MIN_GHOST_SYSCALL.
+ {MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL},
+ {MIN_GHOST_SYSCALL, MAX_SYSCALL},
+#endif
+};
+
+// NextSyscall returns the next system call in the specified system
+// call set after |cur|, or 0 if no such system call exists.
+uint32_t NextSyscall(uint32_t cur, bool invalid_only) {
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (range.first > 0 && cur < range.first - 1) {
+ return range.first - 1;
+ }
+ if (cur <= range.last) {
+ if (invalid_only) {
+ return range.last + 1;
+ }
+ return cur + 1;
+ }
+ }
+
+ // BPF programs only ever operate on unsigned quantities. So, that's how
+ // we iterate; we return values from 0..0xFFFFFFFFu. But there are places,
+ // where the kernel might interpret system call numbers as signed
+ // quantities, so the boundaries between signed and unsigned values are
+ // potential problem cases. We want to explicitly return these values from
+ // our iterator.
+ if (cur < 0x7FFFFFFFu)
+ return 0x7FFFFFFFu;
+ if (cur < 0x80000000u)
+ return 0x80000000u;
+
+ if (cur < 0xFFFFFFFFu)
+ return 0xFFFFFFFFu;
+ return 0;
+}
+
+} // namespace
+
+SyscallSet::Iterator SyscallSet::begin() const {
+ return Iterator(set_, false);
+}
+
+SyscallSet::Iterator SyscallSet::end() const {
+ return Iterator(set_, true);
+}
+
+bool SyscallSet::IsValid(uint32_t num) {
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (num >= range.first && num <= range.last) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool operator==(const SyscallSet& lhs, const SyscallSet& rhs) {
+ return (lhs.set_ == rhs.set_);
+}
+
+SyscallSet::Iterator::Iterator(Set set, bool done)
+ : set_(set), done_(done), num_(0) {
+ if (set_ == Set::INVALID_ONLY && !done_ && IsValid(num_)) {
+ ++*this;
+ }
+}
+
+uint32_t SyscallSet::Iterator::operator*() const {
+ DCHECK(!done_);
+ return num_;
+}
+
+SyscallSet::Iterator& SyscallSet::Iterator::operator++() {
+ DCHECK(!done_);
+
+ num_ = NextSyscall(num_, set_ == Set::INVALID_ONLY);
+ if (num_ == 0) {
+ done_ = true;
+ }
+
+ return *this;
+}
+
+bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ DCHECK(lhs.set_ == rhs.set_);
+ return (lhs.done_ == rhs.done_) && (lhs.num_ == rhs.num_);
+}
+
+bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.h b/sandbox/linux/seccomp-bpf/syscall_iterator.h
new file mode 100644
index 0000000..d89b981
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// TODO(mdempsky): Rename this header to syscall_set.h.
+
+// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
+// iterator is aware of how system calls look like and will skip quickly
+// over ranges that can't contain system calls. It iterates more slowly
+// whenever it reaches a range that is potentially problematic, returning
+// the last invalid value before a valid range of system calls, and the
+// first invalid value after a valid range of syscalls. It iterates over
+// individual values whenever it is in the normal range for system calls
+// (typically MIN_SYSCALL..MAX_SYSCALL).
+//
+// Example usage:
+// for (uint32_t sysnum : SyscallSet::All()) {
+// // Do something with sysnum.
+// }
+class SANDBOX_EXPORT SyscallSet {
+ public:
+ class Iterator;
+
+ SyscallSet(const SyscallSet& ss) : set_(ss.set_) {}
+ ~SyscallSet() {}
+
+ Iterator begin() const;
+ Iterator end() const;
+
+ // All returns a SyscallSet that contains both valid and invalid
+ // system call numbers.
+ static SyscallSet All() { return SyscallSet(Set::ALL); }
+
+ // InvalidOnly returns a SyscallSet that contains only invalid
+ // system call numbers, but still omits numbers in the middle of a
+ // range of invalid system call numbers.
+ static SyscallSet InvalidOnly() { return SyscallSet(Set::INVALID_ONLY); }
+
+ // IsValid returns whether |num| specifies a valid system call
+ // number.
+ static bool IsValid(uint32_t num);
+
+ private:
+ enum class Set { ALL, INVALID_ONLY };
+
+ explicit SyscallSet(Set set) : set_(set) {}
+
+ Set set_;
+
+ friend bool operator==(const SyscallSet&, const SyscallSet&);
+ DISALLOW_ASSIGN(SyscallSet);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet& lhs, const SyscallSet& rhs);
+
+// Iterator provides C++ input iterator semantics for traversing a
+// SyscallSet.
+class SyscallSet::Iterator {
+ public:
+ Iterator(const Iterator& it)
+ : set_(it.set_), done_(it.done_), num_(it.num_) {}
+ ~Iterator() {}
+
+ uint32_t operator*() const;
+ Iterator& operator++();
+
+ private:
+ Iterator(Set set, bool done);
+
+ Set set_;
+ bool done_;
+ uint32_t num_;
+
+ friend SyscallSet;
+ friend bool operator==(const Iterator&, const Iterator&);
+ DISALLOW_ASSIGN(Iterator);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
new file mode 100644
index 0000000..e277f86
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+
+#include <stdint.h>
+
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+const SyscallSet kSyscallSets[] = {
+ SyscallSet::All(),
+ SyscallSet::InvalidOnly(),
+};
+
+SANDBOX_TEST(SyscallIterator, Monotonous) {
+ for (const SyscallSet& set : kSyscallSets) {
+ uint32_t prev = 0;
+ bool have_prev = false;
+ for (uint32_t sysnum : set) {
+ if (have_prev) {
+ SANDBOX_ASSERT(sysnum > prev);
+ } else if (set == SyscallSet::All()) {
+ // The iterator should start at 0.
+ SANDBOX_ASSERT(sysnum == 0);
+ }
+
+ prev = sysnum;
+ have_prev = true;
+ }
+
+ // The iterator should always return 0xFFFFFFFFu as the last value.
+ SANDBOX_ASSERT(have_prev);
+ SANDBOX_ASSERT(prev == 0xFFFFFFFFu);
+ }
+}
+
+// AssertRange checks that SyscallIterator produces all system call
+// numbers in the inclusive range [min, max].
+void AssertRange(uint32_t min, uint32_t max) {
+ SANDBOX_ASSERT(min < max);
+ uint32_t prev = min - 1;
+ for (uint32_t sysnum : SyscallSet::All()) {
+ if (sysnum >= min && sysnum <= max) {
+ SANDBOX_ASSERT(prev == sysnum - 1);
+ prev = sysnum;
+ }
+ }
+ SANDBOX_ASSERT(prev == max);
+}
+
+SANDBOX_TEST(SyscallIterator, ValidSyscallRanges) {
+ AssertRange(MIN_SYSCALL, MAX_PUBLIC_SYSCALL);
+#if defined(__arm__)
+ AssertRange(MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL);
+ AssertRange(MIN_GHOST_SYSCALL, MAX_SYSCALL);
+#endif
+}
+
+SANDBOX_TEST(SyscallIterator, InvalidSyscalls) {
+ static const uint32_t kExpected[] = {
+#if defined(__mips__)
+ 0,
+ MIN_SYSCALL - 1,
+#endif
+ MAX_PUBLIC_SYSCALL + 1,
+#if defined(__arm__)
+ MIN_PRIVATE_SYSCALL - 1,
+ MAX_PRIVATE_SYSCALL + 1,
+ MIN_GHOST_SYSCALL - 1,
+ MAX_SYSCALL + 1,
+#endif
+ 0x7FFFFFFFu,
+ 0x80000000u,
+ 0xFFFFFFFFu,
+ };
+
+ for (const SyscallSet& set : kSyscallSets) {
+ size_t i = 0;
+ for (uint32_t sysnum : set) {
+ if (!SyscallSet::IsValid(sysnum)) {
+ SANDBOX_ASSERT(i < arraysize(kExpected));
+ SANDBOX_ASSERT(kExpected[i] == sysnum);
+ ++i;
+ }
+ }
+ SANDBOX_ASSERT(i == arraysize(kExpected));
+ }
+}
+
+SANDBOX_TEST(SyscallIterator, InvalidOnlyIsOnlyInvalid) {
+ for (uint32_t sysnum : SyscallSet::InvalidOnly()) {
+ SANDBOX_ASSERT(!SyscallSet::IsValid(sysnum));
+ }
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
new file mode 100644
index 0000000..c84d577
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#include <asm/unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::Trap;
+using sandbox::bpf_dsl::SandboxBPFDSLPolicy;
+
+namespace sandbox {
+
+namespace {
+
+// Different platforms use different symbols for the six-argument version
+// of the mmap() system call. Test for the correct symbol at compile time.
+#ifdef __NR_mmap2
+const int kMMapNr = __NR_mmap2;
+#else
+const int kMMapNr = __NR_mmap;
+#endif
+
+TEST(Syscall, InvalidCallReturnsENOSYS) {
+ EXPECT_EQ(-ENOSYS, Syscall::InvalidCall());
+}
+
+TEST(Syscall, WellKnownEntryPoint) {
+// Test that Syscall::Call(-1) is handled specially. Don't do this on ARM,
+// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+// are still testing ARM code in the next set of tests.
+#if !defined(__arm__) && !defined(__aarch64__)
+ EXPECT_NE(Syscall::Call(-1), syscall(-1));
+#endif
+
+// If possible, test that Syscall::Call(-1) returns the address right
+// after
+// a kernel entry point.
+#if defined(__i386__)
+ EXPECT_EQ(0x80CDu, ((uint16_t*)Syscall::Call(-1))[-1]); // INT 0x80
+#elif defined(__x86_64__)
+ EXPECT_EQ(0x050Fu, ((uint16_t*)Syscall::Call(-1))[-1]); // SYSCALL
+#elif defined(__arm__)
+#if defined(__thumb__)
+ EXPECT_EQ(0xDF00u, ((uint16_t*)Syscall::Call(-1))[-1]); // SWI 0
+#else
+ EXPECT_EQ(0xEF000000u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#endif
+#elif defined(__mips__)
+ // Opcode for MIPS sycall is in the lower 16-bits
+ EXPECT_EQ(0x0cu, (((uint32_t*)Syscall::Call(-1))[-1]) & 0x0000FFFF);
+#elif defined(__aarch64__)
+ EXPECT_EQ(0xD4000001u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#else
+#warning Incomplete test case; need port for target platform
+#endif
+}
+
+TEST(Syscall, TrivialSyscallNoArgs) {
+ // Test that we can do basic system calls
+ EXPECT_EQ(Syscall::Call(__NR_getpid), syscall(__NR_getpid));
+}
+
+TEST(Syscall, TrivialSyscallOneArg) {
+ int new_fd;
+ // Duplicate standard error and close it.
+ ASSERT_GE(new_fd = Syscall::Call(__NR_dup, 2), 0);
+ int close_return_value = IGNORE_EINTR(Syscall::Call(__NR_close, new_fd));
+ ASSERT_EQ(close_return_value, 0);
+}
+
+TEST(Syscall, TrivialFailingSyscall) {
+ errno = -42;
+ int ret = Syscall::Call(__NR_dup, -1);
+ ASSERT_EQ(-EBADF, ret);
+ // Verify that Syscall::Call does not touch errno.
+ ASSERT_EQ(-42, errno);
+}
+
+// SIGSYS trap handler that will be called on __NR_uname.
+intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
+ // |aux| is our BPF_AUX pointer.
+ std::vector<uint64_t>* const seen_syscall_args =
+ static_cast<std::vector<uint64_t>*>(aux);
+ BPF_ASSERT(arraysize(args.args) == 6);
+ seen_syscall_args->assign(args.args, args.args + arraysize(args.args));
+ return -ENOMEM;
+}
+
+class CopyAllArgsOnUnamePolicy : public SandboxBPFDSLPolicy {
+ public:
+ explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
+ virtual ~CopyAllArgsOnUnamePolicy() {}
+
+ virtual ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return Trap(CopySyscallArgsToAux, aux_);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ std::vector<uint64_t>* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyAllArgsOnUnamePolicy);
+};
+
+// We are testing Syscall::Call() by making use of a BPF filter that
+// allows us
+// to inspect the system call arguments that the kernel saw.
+BPF_TEST(Syscall,
+ SyntheticSixArgs,
+ CopyAllArgsOnUnamePolicy,
+ std::vector<uint64_t> /* (*BPF_AUX) */) {
+ const int kExpectedValue = 42;
+ // In this test we only pass integers to the kernel. We might want to make
+ // additional tests to try other types. What we will see depends on
+ // implementation details of kernel BPF filters and we will need to document
+ // the expected behavior very clearly.
+ int syscall_args[6];
+ for (size_t i = 0; i < arraysize(syscall_args); ++i) {
+ syscall_args[i] = kExpectedValue + i;
+ }
+
+ // We could use pretty much any system call we don't need here. uname() is
+ // nice because it doesn't have any dangerous side effects.
+ BPF_ASSERT(Syscall::Call(__NR_uname,
+ syscall_args[0],
+ syscall_args[1],
+ syscall_args[2],
+ syscall_args[3],
+ syscall_args[4],
+ syscall_args[5]) == -ENOMEM);
+
+ // We expect the trap handler to have copied the 6 arguments.
+ BPF_ASSERT(BPF_AUX->size() == 6);
+
+ // Don't loop here so that we can see which argument does cause the failure
+ // easily from the failing line.
+ // uint64_t is the type passed to our SIGSYS handler.
+ BPF_ASSERT((*BPF_AUX)[0] == static_cast<uint64_t>(syscall_args[0]));
+ BPF_ASSERT((*BPF_AUX)[1] == static_cast<uint64_t>(syscall_args[1]));
+ BPF_ASSERT((*BPF_AUX)[2] == static_cast<uint64_t>(syscall_args[2]));
+ BPF_ASSERT((*BPF_AUX)[3] == static_cast<uint64_t>(syscall_args[3]));
+ BPF_ASSERT((*BPF_AUX)[4] == static_cast<uint64_t>(syscall_args[4]));
+ BPF_ASSERT((*BPF_AUX)[5] == static_cast<uint64_t>(syscall_args[5]));
+}
+
+TEST(Syscall, ComplexSyscallSixArgs) {
+ int fd;
+ ASSERT_LE(0,
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/dev/null", O_RDWR, 0L));
+
+ // Use mmap() to allocate some read-only memory
+ char* addr0;
+ ASSERT_NE(
+ (char*)NULL,
+ addr0 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 4096,
+ PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ fd,
+ 0L)));
+
+ // Try to replace the existing mapping with a read-write mapping
+ char* addr1;
+ ASSERT_EQ(addr0,
+ addr1 = reinterpret_cast<char*>(
+ Syscall::Call(kMMapNr,
+ addr0,
+ 4096L,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ fd,
+ 0L)));
+ ++*addr1; // This should not seg fault
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+
+ // Check that the offset argument (i.e. the sixth argument) is processed
+ // correctly.
+ ASSERT_GE(
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/proc/self/exe", O_RDONLY, 0L),
+ 0);
+ char* addr2, *addr3;
+ ASSERT_NE((char*)NULL,
+ addr2 = reinterpret_cast<char*>(Syscall::Call(
+ kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
+ ASSERT_NE((char*)NULL,
+ addr3 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 4096L,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+#if defined(__NR_mmap2)
+ 1L
+#else
+ 4096L
+#endif
+ )));
+ EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096));
+
+ // Just to be absolutely on the safe side, also verify that the file
+ // contents matches what we are getting from a read() operation.
+ char buf[8192];
+ EXPECT_EQ(8192, Syscall::Call(__NR_read, fd, buf, 8192L));
+ EXPECT_EQ(0, memcmp(addr2, buf, 8192));
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 8192L));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
new file mode 100644
index 0000000..dce6b7b
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -0,0 +1,388 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+// Android's signal.h doesn't define ucontext etc.
+#if defined(OS_ANDROID)
+#include "sandbox/linux/services/android_ucontext.h"
+#endif
+
+namespace {
+
+struct arch_sigsys {
+ void* ip;
+ int nr;
+ unsigned int arch;
+};
+
+const int kCapacityIncrement = 20;
+
+// Unsafe traps can only be turned on, if the user explicitly allowed them
+// by setting the CHROME_SANDBOX_DEBUGGING environment variable.
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// We need to tell whether we are performing a "normal" callback, or
+// whether we were called recursively from within a UnsafeTrap() callback.
+// This is a little tricky to do, because we need to somehow get access to
+// per-thread data from within a signal context. Normal TLS storage is not
+// safely accessible at this time. We could roll our own, but that involves
+// a lot of complexity. Instead, we co-opt one bit in the signal mask.
+// If BUS is blocked, we assume that we have been called recursively.
+// There is a possibility for collision with other code that needs to do
+// this, but in practice the risks are low.
+// If SIGBUS turns out to be a problem, we could instead co-opt one of the
+// realtime signals. There are plenty of them. Unfortunately, there is no
+// way to mark a signal as allocated. So, the potential for collision is
+// possibly even worse.
+bool GetIsInSigHandler(const ucontext_t* ctx) {
+ // Note: on Android, sigismember does not take a pointer to const.
+ return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), SIGBUS);
+}
+
+void SetIsInSigHandler() {
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, SIGBUS) ||
+ sigprocmask(SIG_BLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to block SIGBUS");
+ }
+}
+
+bool IsDefaultSignalAction(const struct sigaction& sa) {
+ if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+Trap::Trap()
+ : trap_array_(NULL),
+ trap_array_size_(0),
+ trap_array_capacity_(0),
+ has_unsafe_traps_(false) {
+ // Set new SIGSYS handler
+ struct sigaction sa = {};
+ sa.sa_sigaction = SigSysAction;
+ sa.sa_flags = SA_SIGINFO | SA_NODEFER;
+ struct sigaction old_sa;
+ if (sigaction(SIGSYS, &sa, &old_sa) < 0) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+
+ if (!IsDefaultSignalAction(old_sa)) {
+ static const char kExistingSIGSYSMsg[] =
+ "Existing signal handler when trying to install SIGSYS. SIGSYS needs "
+ "to be reserved for seccomp-bpf.";
+ DLOG(FATAL) << kExistingSIGSYSMsg;
+ LOG(ERROR) << kExistingSIGSYSMsg;
+ }
+
+ // Unmask SIGSYS
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, SIGSYS) ||
+ sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+}
+
+bpf_dsl::TrapRegistry* Trap::Registry() {
+ // Note: This class is not thread safe. It is the caller's responsibility
+ // to avoid race conditions. Normally, this is a non-issue as the sandbox
+ // can only be initialized if there are no other threads present.
+ // Also, this is not a normal singleton. Once created, the global trap
+ // object must never be destroyed again.
+ if (!global_trap_) {
+ global_trap_ = new Trap();
+ if (!global_trap_) {
+ SANDBOX_DIE("Failed to allocate global trap handler");
+ }
+ }
+ return global_trap_;
+}
+
+void Trap::SigSysAction(int nr, siginfo_t* info, void* void_context) {
+ if (!global_trap_) {
+ RAW_SANDBOX_DIE(
+ "This can't happen. Found no global singleton instance "
+ "for Trap() handling.");
+ }
+ global_trap_->SigSys(nr, info, void_context);
+}
+
+void Trap::SigSys(int nr, siginfo_t* info, void* void_context) {
+ // Signal handlers should always preserve "errno". Otherwise, we could
+ // trigger really subtle bugs.
+ const int old_errno = errno;
+
+ // Various sanity checks to make sure we actually received a signal
+ // triggered by a BPF filter. If something else triggered SIGSYS
+ // (e.g. kill()), there is really nothing we can do with this signal.
+ if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context ||
+ info->si_errno <= 0 ||
+ static_cast<size_t>(info->si_errno) > trap_array_size_) {
+ // ATI drivers seem to send SIGSYS, so this cannot be FATAL.
+ // See crbug.com/178166.
+ // TODO(jln): add a DCHECK or move back to FATAL.
+ RAW_LOG(ERROR, "Unexpected SIGSYS received.");
+ errno = old_errno;
+ return;
+ }
+
+ // Obtain the signal context. This, most notably, gives us access to
+ // all CPU registers at the time of the signal.
+ ucontext_t* ctx = reinterpret_cast<ucontext_t*>(void_context);
+
+ // Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
+ // most versions of glibc don't include this information in siginfo_t. So,
+ // we need to explicitly copy it into a arch_sigsys structure.
+ struct arch_sigsys sigsys;
+ memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
+
+#if defined(__mips__)
+ // When indirect syscall (syscall(__NR_foo, ...)) is made on Mips, the
+ // number in register SECCOMP_SYSCALL(ctx) is always __NR_syscall and the
+ // real number of a syscall (__NR_foo) is in SECCOMP_PARM1(ctx)
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) &&
+ sigsys.nr != static_cast<int>(SECCOMP_PARM1(ctx));
+#else
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx));
+#endif
+
+ // Some more sanity checks.
+ if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
+ sigsys_nr_is_bad || sigsys.arch != SECCOMP_ARCH) {
+ // TODO(markus):
+ // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
+ // safe and can lead to bugs. We should eventually implement a different
+ // logging and reporting mechanism that is safe to be called from
+ // the sigSys() handler.
+ RAW_SANDBOX_DIE("Sanity checks are failing after receiving SIGSYS.");
+ }
+
+ intptr_t rc;
+ if (has_unsafe_traps_ && GetIsInSigHandler(ctx)) {
+ errno = old_errno;
+ if (sigsys.nr == __NR_clone) {
+ RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
+ }
+#if defined(__mips__)
+ // Mips supports up to eight arguments for syscall.
+ // However, seccomp bpf can filter only up to six arguments, so using eight
+ // arguments has sense only when using UnsafeTrap() handler.
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx),
+ SECCOMP_PARM7(ctx),
+ SECCOMP_PARM8(ctx));
+#else
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx));
+#endif // defined(__mips__)
+ } else {
+ const TrapKey& trap = trap_array_[info->si_errno - 1];
+ if (!trap.safe) {
+ SetIsInSigHandler();
+ }
+
+ // Copy the seccomp-specific data into a arch_seccomp_data structure. This
+ // is what we are showing to TrapFnc callbacks that the system call
+ // evaluator registered with the sandbox.
+ struct arch_seccomp_data data = {
+ static_cast<int>(SECCOMP_SYSCALL(ctx)),
+ SECCOMP_ARCH,
+ reinterpret_cast<uint64_t>(sigsys.ip),
+ {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM6(ctx))}};
+
+ // Now call the TrapFnc callback associated with this particular instance
+ // of SECCOMP_RET_TRAP.
+ rc = trap.fnc(data, const_cast<void*>(trap.aux));
+ }
+
+ // Update the CPU register that stores the return code of the system call
+ // that we just handled, and restore "errno" to the value that it had
+ // before entering the signal handler.
+ Syscall::PutValueInUcontext(rc, ctx);
+ errno = old_errno;
+
+ return;
+}
+
+bool Trap::TrapKey::operator<(const TrapKey& o) const {
+ if (fnc != o.fnc) {
+ return fnc < o.fnc;
+ } else if (aux != o.aux) {
+ return aux < o.aux;
+ } else {
+ return safe < o.safe;
+ }
+}
+
+uint16_t Trap::MakeTrap(TrapFnc fnc, const void* aux, bool safe) {
+ return Registry()->Add(fnc, aux, safe);
+}
+
+uint16_t Trap::Add(TrapFnc fnc, const void* aux, bool safe) {
+ if (!safe && !SandboxDebuggingAllowedByUser()) {
+ // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
+ // we never return an ErrorCode that is marked as "unsafe". This also
+ // means, the BPF compiler will never emit code that allow unsafe system
+ // calls to by-pass the filter (because they use the magic return address
+ // from Syscall::Call(-1)).
+
+ // This SANDBOX_DIE() can optionally be removed. It won't break security,
+ // but it might make error messages from the BPF compiler a little harder
+ // to understand. Removing the SANDBOX_DIE() allows callers to easily check
+ // whether unsafe traps are supported (by checking whether the returned
+ // ErrorCode is ET_INVALID).
+ SANDBOX_DIE(
+ "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
+ "is enabled");
+
+ return 0;
+ }
+
+ // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
+ // of a SECCOMP_RET_TRAP.
+ TrapKey key(fnc, aux, safe);
+
+ // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
+ // us to associate trap with the appropriate handler. The kernel allows us
+ // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
+ // avoid 0, as it could be confused for a trap without any specific id.
+ // The nice thing about sequentially numbered identifiers is that we can also
+ // trivially look them up from our signal handler without making any system
+ // calls that might be async-signal-unsafe.
+ // In order to do so, we store all of our traps in a C-style trap_array_.
+
+ TrapIds::const_iterator iter = trap_ids_.find(key);
+ if (iter != trap_ids_.end()) {
+ // We have seen this pair before. Return the same id that we assigned
+ // earlier.
+ return iter->second;
+ }
+
+ // This is a new pair. Remember it and assign a new id.
+ if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
+ trap_array_size_ >= std::numeric_limits<uint16_t>::max()) {
+ // In practice, this is pretty much impossible to trigger, as there
+ // are other kernel limitations that restrict overall BPF program sizes.
+ SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
+ }
+
+ // Our callers ensure that there are no other threads accessing trap_array_
+ // concurrently (typically this is done by ensuring that we are single-
+ // threaded while the sandbox is being set up). But we nonetheless are
+ // modifying a live data structure that could be accessed any time a
+ // system call is made; as system calls could be triggering SIGSYS.
+ // So, we have to be extra careful that we update trap_array_ atomically.
+ // In particular, this means we shouldn't be using realloc() to resize it.
+ // Instead, we allocate a new array, copy the values, and then switch the
+ // pointer. We only really care about the pointer being updated atomically
+ // and the data that is pointed to being valid, as these are the only
+ // values accessed from the signal handler. It is OK if trap_array_size_
+ // is inconsistent with the pointer, as it is monotonously increasing.
+ // Also, we only care about compiler barriers, as the signal handler is
+ // triggered synchronously from a system call. We don't have to protect
+ // against issues with the memory model or with completely asynchronous
+ // events.
+ if (trap_array_size_ >= trap_array_capacity_) {
+ trap_array_capacity_ += kCapacityIncrement;
+ TrapKey* old_trap_array = trap_array_;
+ TrapKey* new_trap_array = new TrapKey[trap_array_capacity_];
+ std::copy_n(old_trap_array, trap_array_size_, new_trap_array);
+
+ // Language specs are unclear on whether the compiler is allowed to move
+ // the "delete[]" above our preceding assignments and/or memory moves,
+ // iff the compiler believes that "delete[]" doesn't have any other
+ // global side-effects.
+ // We insert optimization barriers to prevent this from happening.
+ // The first barrier is probably not needed, but better be explicit in
+ // what we want to tell the compiler.
+ // The clang developer mailing list couldn't answer whether this is a
+ // legitimate worry; but they at least thought that the barrier is
+ // sufficient to prevent the (so far hypothetical) problem of re-ordering
+ // of instructions by the compiler.
+ //
+ // TODO(mdempsky): Try to clean this up using base/atomicops or C++11
+ // atomics; see crbug.com/414363.
+ asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
+ trap_array_ = new_trap_array;
+ asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
+
+ delete[] old_trap_array;
+ }
+
+ uint16_t id = trap_array_size_ + 1;
+ trap_ids_[key] = id;
+ trap_array_[trap_array_size_] = key;
+ trap_array_size_++;
+ return id;
+}
+
+bool Trap::SandboxDebuggingAllowedByUser() const {
+ const char* debug_flag = getenv(kSandboxDebuggingEnv);
+ return debug_flag && *debug_flag;
+}
+
+bool Trap::EnableUnsafeTrapsInSigSysHandler() {
+ return Registry()->EnableUnsafeTraps();
+}
+
+bool Trap::EnableUnsafeTraps() {
+ if (!has_unsafe_traps_) {
+ // Unsafe traps are a one-way fuse. Once enabled, they can never be turned
+ // off again.
+ // We only allow enabling unsafe traps, if the user explicitly set an
+ // appropriate environment variable. This prevents bugs that accidentally
+ // disable all sandboxing for all users.
+ if (SandboxDebuggingAllowedByUser()) {
+ // We only ever print this message once, when we enable unsafe traps the
+ // first time.
+ SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
+ has_unsafe_traps_ = true;
+ } else {
+ SANDBOX_INFO(
+ "Cannot disable sandbox and use unsafe traps unless "
+ "CHROME_SANDBOX_DEBUGGING is turned on first");
+ }
+ }
+ // Returns the, possibly updated, value of has_unsafe_traps_.
+ return has_unsafe_traps_;
+}
+
+Trap* Trap::global_trap_;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/trap.h b/sandbox/linux/seccomp-bpf/trap.h
new file mode 100644
index 0000000..0a184e6
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+
+#include <signal.h>
+#include <stdint.h>
+
+#include <map>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// The Trap class allows a BPF filter program to branch out to user space by
+// raising a SIGSYS signal.
+// N.B.: This class does not perform any synchronization operations. If
+// modifications are made to any of the traps, it is the caller's
+// responsibility to ensure that this happens in a thread-safe fashion.
+// Preferably, that means that no other threads should be running at that
+// time. For the purposes of our sandbox, this assertion should always be
+// true. Threads are incompatible with the seccomp sandbox anyway.
+class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry {
+ public:
+ virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override;
+
+ virtual bool EnableUnsafeTraps() override;
+
+ // Registry returns the trap registry used by Trap's SIGSYS handler,
+ // creating it if necessary.
+ static bpf_dsl::TrapRegistry* Registry();
+
+ // Registers a new trap handler and sets up the appropriate SIGSYS handler
+ // as needed.
+ // N.B.: This makes a permanent state change. Traps cannot be unregistered,
+ // as that would break existing BPF filters that are still active.
+ // TODO(mdempsky): Deprecated; remove.
+ static uint16_t MakeTrap(TrapFnc fnc, const void* aux, bool safe);
+
+ // Enables support for unsafe traps in the SIGSYS signal handler. This is a
+ // one-way fuse. It works in conjunction with the BPF compiler emitting code
+ // that unconditionally allows system calls, if they have a magic return
+ // address (i.e. SandboxSyscall(-1)).
+ // Once unsafe traps are enabled, the sandbox is essentially compromised.
+ // But this is still a very useful feature for debugging purposes. Use with
+ // care. This feature is availably only if enabled by the user (see above).
+ // Returns "true", if unsafe traps were turned on.
+ // TODO(mdempsky): Deprecated; remove.
+ static bool EnableUnsafeTrapsInSigSysHandler();
+
+ private:
+ struct TrapKey {
+ TrapKey() : fnc(NULL), aux(NULL), safe(false) {}
+ TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {}
+ TrapFnc fnc;
+ const void* aux;
+ bool safe;
+ bool operator<(const TrapKey&) const;
+ };
+ typedef std::map<TrapKey, uint16_t> TrapIds;
+
+ // Our constructor is private. A shared global instance is created
+ // automatically as needed.
+ Trap();
+
+ // The destructor is unimplemented. Don't ever attempt to destruct this
+ // object. It'll break subsequent system calls that trigger a SIGSYS.
+ ~Trap();
+
+ static void SigSysAction(int nr, siginfo_t* info, void* void_context);
+
+ // Make sure that SigSys is not inlined in order to get slightly better crash
+ // dumps.
+ void SigSys(int nr, siginfo_t* info, void* void_context)
+ __attribute__((noinline));
+ bool SandboxDebuggingAllowedByUser() const;
+
+ // We have a global singleton that handles all of our SIGSYS traps. This
+ // variable must never be deallocated after it has been set up initially, as
+ // there is no way to reset in-kernel BPF filters that generate SIGSYS
+ // events.
+ static Trap* global_trap_;
+
+ TrapIds trap_ids_; // Maps from TrapKeys to numeric ids
+ TrapKey* trap_array_; // Array of TrapKeys indexed by ids
+ size_t trap_array_size_; // Currently used size of array
+ size_t trap_array_capacity_; // Currently allocated capacity of array
+ bool has_unsafe_traps_; // Whether unsafe traps have been enabled
+
+ // Copying and assigning is unimplemented. It doesn't make sense for a
+ // singleton.
+ DISALLOW_COPY_AND_ASSIGN(Trap);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
new file mode 100644
index 0000000..0c344b0
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -0,0 +1,400 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/verifier.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+
+namespace sandbox {
+
+namespace {
+
+const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max();
+const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32;
+const uint64_t kFull64Bits = std::numeric_limits<uint64_t>::max();
+
+struct State {
+ State(const std::vector<struct sock_filter>& p,
+ const struct arch_seccomp_data& d)
+ : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {}
+ const std::vector<struct sock_filter>& program;
+ const struct arch_seccomp_data& data;
+ unsigned int ip;
+ uint32_t accumulator;
+ bool acc_is_valid;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(State);
+};
+
+uint32_t EvaluateErrorCode(bpf_dsl::PolicyCompiler* compiler,
+ const ErrorCode& code,
+ const struct arch_seccomp_data& data) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ return code.err();
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.width() == ErrorCode::TP_32BIT &&
+ (data.args[code.argno()] >> 32) &&
+ (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
+ 0xFFFFFFFF80000000ull) {
+ return compiler->Unexpected64bitArgument().err();
+ }
+ bool equal = (data.args[code.argno()] & code.mask()) == code.value();
+ return EvaluateErrorCode(
+ compiler, equal ? *code.passed() : *code.failed(), data);
+ } else {
+ return SECCOMP_RET_INVALID;
+ }
+}
+
+bool VerifyErrorCode(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ struct arch_seccomp_data* data,
+ const ErrorCode& root_code,
+ const ErrorCode& code,
+ const char** err) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
+ if (*err) {
+ return false;
+ } else if (computed_ret != EvaluateErrorCode(compiler, root_code, *data)) {
+ // For efficiency's sake, we'd much rather compare "computed_ret"
+ // against "code.err()". This works most of the time, but it doesn't
+ // always work for nested conditional expressions. The test values
+ // that we generate on the fly to probe expressions can trigger
+ // code flow decisions in multiple nodes of the decision tree, and the
+ // only way to compute the correct error code in that situation is by
+ // calling EvaluateErrorCode().
+ *err = "Exit code from BPF program doesn't match";
+ return false;
+ }
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.argno() < 0 || code.argno() >= 6) {
+ *err = "Invalid argument number in error code";
+ return false;
+ }
+
+ // TODO(mdempsky): The test values generated here try to provide good
+ // coverage for generated BPF instructions while avoiding combinatorial
+ // explosion on large policies. Ideally we would instead take a fuzzing-like
+ // approach and generate a bounded number of test cases regardless of policy
+ // size.
+
+ // Verify that we can check a value for simple equality.
+ data->args[code.argno()] = code.value();
+ if (!VerifyErrorCode(
+ compiler, program, data, root_code, *code.passed(), err)) {
+ return false;
+ }
+
+ // If mask ignores any bits, verify that setting those bits is still
+ // detected as equality.
+ uint64_t ignored_bits = ~code.mask();
+ if (code.width() == ErrorCode::TP_32BIT) {
+ ignored_bits = static_cast<uint32_t>(ignored_bits);
+ }
+ if ((ignored_bits & kLower32Bits) != 0) {
+ data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits);
+ if (!VerifyErrorCode(
+ compiler, program, data, root_code, *code.passed(), err)) {
+ return false;
+ }
+ }
+ if ((ignored_bits & kUpper32Bits) != 0) {
+ data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits);
+ if (!VerifyErrorCode(
+ compiler, program, data, root_code, *code.passed(), err)) {
+ return false;
+ }
+ }
+
+ // Verify that changing bits included in the mask is detected as inequality.
+ if ((code.mask() & kLower32Bits) != 0) {
+ data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits);
+ if (!VerifyErrorCode(
+ compiler, program, data, root_code, *code.failed(), err)) {
+ return false;
+ }
+ }
+ if ((code.mask() & kUpper32Bits) != 0) {
+ data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits);
+ if (!VerifyErrorCode(
+ compiler, program, data, root_code, *code.failed(), err)) {
+ return false;
+ }
+ }
+
+ if (code.width() == ErrorCode::TP_32BIT) {
+ // For 32-bit system call arguments, we emit additional instructions to
+ // validate the upper 32-bits. Here we test that validation.
+
+ // Arbitrary 64-bit values should be rejected.
+ data->args[code.argno()] = 1ULL << 32;
+ if (!VerifyErrorCode(compiler,
+ program,
+ data,
+ root_code,
+ compiler->Unexpected64bitArgument(),
+ err)) {
+ return false;
+ }
+
+ // Upper 32-bits set without the MSB of the lower 32-bits set should be
+ // rejected too.
+ data->args[code.argno()] = kUpper32Bits;
+ if (!VerifyErrorCode(compiler,
+ program,
+ data,
+ root_code,
+ compiler->Unexpected64bitArgument(),
+ err)) {
+ return false;
+ }
+ }
+ } else {
+ *err = "Attempting to return invalid error code from BPF program";
+ return false;
+ }
+ return true;
+}
+
+void Ld(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_SIZE(insn.code) != BPF_W || BPF_MODE(insn.code) != BPF_ABS ||
+ insn.jt != 0 || insn.jf != 0) {
+ *err = "Invalid BPF_LD instruction";
+ return;
+ }
+ if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
+ // We only allow loading of properly aligned 32bit quantities.
+ memcpy(&state->accumulator,
+ reinterpret_cast<const char*>(&state->data) + insn.k,
+ 4);
+ } else {
+ *err = "Invalid operand in BPF_LD instruction";
+ return;
+ }
+ state->acc_is_valid = true;
+ return;
+}
+
+void Jmp(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ if (state->ip + insn.k + 1 >= state->program.size() ||
+ state->ip + insn.k + 1 <= state->ip) {
+ compilation_failure:
+ *err = "Invalid BPF_JMP instruction";
+ return;
+ }
+ state->ip += insn.k;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K || !state->acc_is_valid ||
+ state->ip + insn.jt + 1 >= state->program.size() ||
+ state->ip + insn.jf + 1 >= state->program.size()) {
+ goto compilation_failure;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_JEQ:
+ if (state->accumulator == insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGT:
+ if (state->accumulator > insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGE:
+ if (state->accumulator >= insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JSET:
+ if (state->accumulator & insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ default:
+ goto compilation_failure;
+ }
+ }
+}
+
+uint32_t Ret(State*, const struct sock_filter& insn, const char** err) {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Invalid BPF_RET instruction";
+ return 0;
+ }
+ return insn.k;
+}
+
+void Alu(State* state, const struct sock_filter& insn, const char** err) {
+ if (BPF_OP(insn.code) == BPF_NEG) {
+ state->accumulator = -state->accumulator;
+ return;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Unexpected source operand in arithmetic operation";
+ return;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_ADD:
+ state->accumulator += insn.k;
+ break;
+ case BPF_SUB:
+ state->accumulator -= insn.k;
+ break;
+ case BPF_MUL:
+ state->accumulator *= insn.k;
+ break;
+ case BPF_DIV:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator /= insn.k;
+ break;
+ case BPF_MOD:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator %= insn.k;
+ break;
+ case BPF_OR:
+ state->accumulator |= insn.k;
+ break;
+ case BPF_XOR:
+ state->accumulator ^= insn.k;
+ break;
+ case BPF_AND:
+ state->accumulator &= insn.k;
+ break;
+ case BPF_LSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator <<= insn.k;
+ break;
+ case BPF_RSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator >>= insn.k;
+ break;
+ default:
+ *err = "Invalid operator in arithmetic operation";
+ break;
+ }
+ }
+}
+
+} // namespace
+
+bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ const bpf_dsl::SandboxBPFDSLPolicy& policy,
+ const char** err) {
+ *err = NULL;
+ for (uint32_t sysnum : SyscallSet::All()) {
+ // We ideally want to iterate over the full system call range and values
+ // just above and just below this range. This gives us the full result set
+ // of the "evaluators".
+ // On Intel systems, this can fail in a surprising way, as a cleared bit 30
+ // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
+ // unless we pay attention to setting this bit correctly, an early check in
+ // our BPF program will make us fail with a misleading error code.
+ struct arch_seccomp_data data = {static_cast<int>(sysnum),
+ static_cast<uint32_t>(SECCOMP_ARCH)};
+#if defined(__i386__) || defined(__x86_64__)
+#if defined(__x86_64__) && defined(__ILP32__)
+ if (!(sysnum & 0x40000000u)) {
+ continue;
+ }
+#else
+ if (sysnum & 0x40000000u) {
+ continue;
+ }
+#endif
+#endif
+ ErrorCode code = SyscallSet::IsValid(sysnum)
+ ? policy.EvaluateSyscall(sysnum)->Compile(compiler)
+ : policy.InvalidSyscall()->Compile(compiler);
+ if (!VerifyErrorCode(compiler, program, &data, code, code, err)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char** err) {
+ *err = NULL;
+ if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
+ *err = "Invalid program length";
+ return 0;
+ }
+ for (State state(program, data); !*err; ++state.ip) {
+ if (state.ip >= program.size()) {
+ *err = "Invalid instruction pointer in BPF program";
+ break;
+ }
+ const struct sock_filter& insn = program[state.ip];
+ switch (BPF_CLASS(insn.code)) {
+ case BPF_LD:
+ Ld(&state, insn, err);
+ break;
+ case BPF_JMP:
+ Jmp(&state, insn, err);
+ break;
+ case BPF_RET: {
+ uint32_t r = Ret(&state, insn, err);
+ switch (r & SECCOMP_RET_ACTION) {
+ case SECCOMP_RET_TRAP:
+ case SECCOMP_RET_ERRNO:
+ case SECCOMP_RET_TRACE:
+ case SECCOMP_RET_ALLOW:
+ break;
+ case SECCOMP_RET_KILL: // We don't ever generate this
+ case SECCOMP_RET_INVALID: // Should never show up in BPF program
+ default:
+ *err = "Unexpected return code found in BPF program";
+ return 0;
+ }
+ return r;
+ }
+ case BPF_ALU:
+ Alu(&state, insn, err);
+ break;
+ default:
+ *err = "Unexpected instruction in BPF program";
+ break;
+ }
+ }
+ return 0;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h
new file mode 100644
index 0000000..66a3ee0
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/verifier.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+
+struct sock_filter;
+
+namespace sandbox {
+struct arch_seccomp_data;
+namespace bpf_dsl {
+class SandboxBPFDSLPolicy;
+class PolicyCompiler;
+}
+
+class Verifier {
+ public:
+ // Evaluate the BPF program for all possible inputs and verify that it
+ // computes the correct result. We use the "evaluators" to determine
+ // the full set of possible inputs that we have to iterate over.
+ // Returns success, if the BPF filter accurately reflects the rules
+ // set by the "evaluators".
+ // Upon success, "err" is set to NULL. Upon failure, it contains a static
+ // error message that does not need to be free()'d.
+ static bool VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ const bpf_dsl::SandboxBPFDSLPolicy& policy,
+ const char** err);
+
+ // Evaluate a given BPF program for a particular set of system call
+ // parameters. If evaluation failed for any reason, "err" will be set to
+ // a non-NULL error string. Otherwise, the BPF program's result will be
+ // returned by the function and "err" is NULL.
+ // We do not actually implement the full BPF state machine, but only the
+ // parts that can actually be generated by our BPF compiler. If this code
+ // is used for purposes other than verifying the output of the sandbox's
+ // BPF compiler, we might have to extend this BPF interpreter.
+ static uint32_t EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char** err);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
diff --git a/sandbox/linux/services/android_arm64_ucontext.h b/sandbox/linux/services/android_arm64_ucontext.h
new file mode 100644
index 0000000..3df0399
--- /dev/null
+++ b/sandbox/linux/services/android_arm64_ucontext.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_ARM64_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_ARM64_UCONTEXT_H_
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+#include <signal.h>
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef uint64_t greg_t;
+
+struct ucontext_t {
+ unsigned long uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ sigset_t uc_sigmask;
+ /* glibc uses a 1024-bit sigset_t */
+ uint8_t unused[1024 / 8 - sizeof(sigset_t)];
+ /* last for future expansion */
+ struct sigcontext uc_mcontext;
+};
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_ARM64_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_arm_ucontext.h b/sandbox/linux/services/android_arm_ucontext.h
new file mode 100644
index 0000000..d1446c6
--- /dev/null
+++ b/sandbox/linux/services/android_arm_ucontext.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef unsigned long greg_t;
+
+//typedef unsigned long sigset_t;
+typedef struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ sigset_t uc_sigmask;
+ /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */
+ int __not_used[32 - (sizeof (sigset_t) / sizeof (int))];
+ /* Last for extensibility. Eight byte aligned because some
+ coprocessors require eight byte alignment. */
+ unsigned long uc_regspace[128] __attribute__((__aligned__(8)));
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_futex.h b/sandbox/linux/services/android_futex.h
new file mode 100644
index 0000000..571f5d2
--- /dev/null
+++ b/sandbox/linux/services/android_futex.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_
+
+#if !defined(FUTEX_WAIT)
+#define FUTEX_WAIT 0
+#endif
+
+#if !defined(FUTEX_WAKE)
+#define FUTEX_WAKE 1
+#endif
+
+#if !defined(FUTEX_FD)
+#define FUTEX_FD 2
+#endif
+
+#if !defined(FUTEX_REQUEUE)
+#define FUTEX_REQUEUE 3
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE)
+#define FUTEX_CMP_REQUEUE 4
+#endif
+
+#if !defined(FUTEX_WAKE_OP)
+#define FUTEX_WAKE_OP 5
+#endif
+
+#if !defined(FUTEX_LOCK_PI)
+#define FUTEX_LOCK_PI 6
+#endif
+
+#if !defined(FUTEX_UNLOCK_PI)
+#define FUTEX_UNLOCK_PI 7
+#endif
+
+#if !defined(FUTEX_TRYLOCK_PI)
+#define FUTEX_TRYLOCK_PI 8
+#endif
+
+#if !defined(FUTEX_WAIT_BITSET)
+#define FUTEX_WAIT_BITSET 9
+#endif
+
+#if !defined(FUTEX_WAKE_BITSET)
+#define FUTEX_WAKE_BITSET 10
+#endif
+
+#if !defined(FUTEX_WAIT_REQUEUE_PI)
+#define FUTEX_WAIT_REQUEUE_PI 11
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI)
+#define FUTEX_CMP_REQUEUE_PI 12
+#endif
+
+#if !defined(FUTEX_PRIVATE_FLAG)
+#define FUTEX_PRIVATE_FLAG 128
+#endif
+
+#if !defined FUTEX_CLOCK_REALTIME
+#define FUTEX_CLOCK_REALTIME 256
+#endif
+
+#if !defined(FUTEX_CMD_MASK)
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI_PRIVATE)
+#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#if !defined(FUTEX_UNLOCK_PI_PRIVATE)
+#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_
diff --git a/sandbox/linux/services/android_i386_ucontext.h b/sandbox/linux/services/android_i386_ucontext.h
new file mode 100644
index 0000000..580ac70
--- /dev/null
+++ b/sandbox/linux/services/android_i386_ucontext.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_
+
+// We do something compatible with glibc. Hopefully, at some point Android will
+// provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined.
+// This is mostly copied from breakpad (common/android/include/sys/ucontext.h),
+// except we do use sigset_t for uc_sigmask instead of a custom type.
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+/* 80-bit floating-point register */
+struct _libc_fpreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+};
+
+/* Simple floating-point state, see FNSTENV instruction */
+struct _libc_fpstate {
+ unsigned long cw;
+ unsigned long sw;
+ unsigned long tag;
+ unsigned long ipoff;
+ unsigned long cssel;
+ unsigned long dataoff;
+ unsigned long datasel;
+ struct _libc_fpreg _st[8];
+ unsigned long status;
+};
+
+typedef uint32_t greg_t;
+
+typedef struct {
+ uint32_t gregs[19];
+ struct _libc_fpstate* fpregs;
+ uint32_t oldmask;
+ uint32_t cr2;
+} mcontext_t;
+
+enum {
+ REG_GS = 0,
+ REG_FS,
+ REG_ES,
+ REG_DS,
+ REG_EDI,
+ REG_ESI,
+ REG_EBP,
+ REG_ESP,
+ REG_EBX,
+ REG_EDX,
+ REG_ECX,
+ REG_EAX,
+ REG_TRAPNO,
+ REG_ERR,
+ REG_EIP,
+ REG_CS,
+ REG_EFL,
+ REG_UESP,
+ REG_SS,
+};
+
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_mips_ucontext.h b/sandbox/linux/services/android_mips_ucontext.h
new file mode 100644
index 0000000..e23f1a7
--- /dev/null
+++ b/sandbox/linux/services/android_mips_ucontext.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_MIPS_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_MIPS_UCONTEXT_H_
+
+// This is mostly copied from breakpad (common/android/include/sys/ucontext.h),
+// except we do use sigset_t for uc_sigmask instead of a custom type.
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+// Ensure that 'stack_t' is defined.
+#include <asm/signal.h>
+
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef unsigned long greg_t;
+
+typedef struct {
+ uint32_t regmask;
+ uint32_t status;
+ uint64_t pc;
+ uint64_t gregs[32];
+ uint64_t fpregs[32];
+ uint32_t acx;
+ uint32_t fpc_csr;
+ uint32_t fpc_eir;
+ uint32_t used_math;
+ uint32_t dsp;
+ uint64_t mdhi;
+ uint64_t mdlo;
+ uint32_t hi1;
+ uint32_t lo1;
+ uint32_t hi2;
+ uint32_t lo2;
+ uint32_t hi3;
+ uint32_t lo3;
+} mcontext_t;
+
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ // Other fields are not used by Google Breakpad. Don't define them.
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_MIPS_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_ucontext.h b/sandbox/linux/services/android_ucontext.h
new file mode 100644
index 0000000..2814710
--- /dev/null
+++ b/sandbox/linux/services/android_ucontext.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
+
+#if defined(__ANDROID__)
+
+#if defined(__arm__)
+#include "sandbox/linux/services/android_arm_ucontext.h"
+#elif defined(__i386__)
+#include "sandbox/linux/services/android_i386_ucontext.h"
+#elif defined(__x86_64__)
+#include "sandbox/linux/services/android_x86_64_ucontext.h"
+#elif defined(__mips__)
+#include "sandbox/linux/services/android_mips_ucontext.h"
+#elif defined(__aarch64__)
+#include "sandbox/linux/services/android_arm64_ucontext.h"
+#else
+#error "No support for your architecture in Android header"
+#endif
+
+#else // __ANDROID__
+#error "Android header file included on non Android."
+#endif // __ANDROID__
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_x86_64_ucontext.h b/sandbox/linux/services/android_x86_64_ucontext.h
new file mode 100644
index 0000000..ef328e5
--- /dev/null
+++ b/sandbox/linux/services/android_x86_64_ucontext.h
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_
+
+// We do something compatible with glibc. Hopefully, at some point Android will
+// provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined.
+// Spec:
+// http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/libc-ddefs.html#AEN5668
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+struct _libc_fpxreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+ unsigned short padding[3];
+};
+
+struct _libc_xmmreg {
+ uint32_t element[4];
+};
+
+struct _libc_fpstate {
+ uint16_t cwd;
+ uint16_t swd;
+ uint16_t twd;
+ uint16_t fop;
+ uint64_t rip;
+ uint64_t rdp;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ struct _libc_fpxreg _st[8];
+ struct _libc_xmmreg _xmm[16];
+ uint32_t padding[24];
+};
+
+typedef uint64_t greg_t;
+
+typedef struct {
+ greg_t gregs[23];
+ struct _libc_fpstate* fpregs;
+ unsigned long __reserved1[8];
+} mcontext_t;
+
+enum {
+ REG_R8 = 0,
+ REG_R9,
+ REG_R10,
+ REG_R11,
+ REG_R12,
+ REG_R13,
+ REG_R14,
+ REG_R15,
+ REG_RDI,
+ REG_RSI,
+ REG_RBP,
+ REG_RBX,
+ REG_RDX,
+ REG_RAX,
+ REG_RCX,
+ REG_RSP,
+ REG_RIP,
+ REG_EFL,
+ REG_CSGSFS,
+ REG_ERR,
+ REG_TRAPNO,
+ REG_OLDMASK,
+ REG_CR2,
+ NGREG,
+};
+
+typedef struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_
diff --git a/sandbox/linux/services/arm64_linux_syscalls.h b/sandbox/linux/services/arm64_linux_syscalls.h
new file mode 100644
index 0000000..4443059
--- /dev/null
+++ b/sandbox/linux/services/arm64_linux_syscalls.h
@@ -0,0 +1,1062 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ARM64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_ARM64_LINUX_SYSCALLS_H_
+
+#include <asm-generic/unistd.h>
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 0
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 1
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 2
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 3
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 4
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 5
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 6
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 7
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 8
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 9
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 10
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 11
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 12
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 13
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 14
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 15
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 16
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 17
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 18
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 19
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 20
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 21
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 22
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 23
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 24
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 25
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 26
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 27
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 28
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 29
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 30
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 31
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 32
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 33
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 34
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 35
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 36
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 37
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 38
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 39
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 40
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 41
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 42
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 43
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 44
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 45
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 46
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 47
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 48
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 49
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 50
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 51
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 52
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 53
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 54
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 55
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 56
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 57
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 58
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 59
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 60
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 61
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 62
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 63
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 64
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 65
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 66
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 67
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 68
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 69
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 70
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 71
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 72
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 73
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 74
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 75
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 76
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 77
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 78
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat 79
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 80
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 81
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 82
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 83
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 84
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 85
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 86
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 87
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 88
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 89
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 90
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 91
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 92
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 93
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 94
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 95
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 96
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 97
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 98
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 99
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 100
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 101
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 102
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 103
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 104
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 105
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 106
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 107
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 108
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 109
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 110
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 111
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 112
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 113
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 114
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 115
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 116
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 117
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 118
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 119
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 120
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 121
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 122
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 123
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 124
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 125
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 126
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 127
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 128
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 129
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 130
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 131
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 132
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 133
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 134
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 135
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 136
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 137
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 138
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 139
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 140
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 141
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 142
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 143
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 144
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 145
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 146
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 147
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 148
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 149
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 150
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 151
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 152
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 153
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 154
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 155
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 156
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 157
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 158
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 159
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 160
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 161
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 162
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 163
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 164
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 165
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 166
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 167
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 168
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 169
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 170
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 171
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 172
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 173
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 174
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 175
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 176
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 177
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 178
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 179
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 180
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 181
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 182
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 183
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 184
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 185
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 186
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 187
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 188
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 189
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 190
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 191
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop 192
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop 193
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 194
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 195
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 196
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 197
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 198
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 199
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 200
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 201
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept 202
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 203
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 204
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 205
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 206
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 207
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 208
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 209
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 210
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 211
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 212
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 213
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 214
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 215
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 216
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 217
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 218
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 219
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 220
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 221
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 222
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 223
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 224
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 225
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 226
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 227
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 228
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 229
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 230
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 231
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 232
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 233
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 234
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 235
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 236
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 237
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 238
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 239
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 240
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 241
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 242
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 243
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 260
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 261
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 262
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 263
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 264
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 265
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 266
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 267
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 268
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 269
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 270
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 271
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 272
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 273
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 274
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 275
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 276
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 277
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 278
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_ARM64_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/services/arm_linux_syscalls.h b/sandbox/linux/services/arm_linux_syscalls.h
new file mode 100644
index 0000000..5fa140d
--- /dev/null
+++ b/sandbox/linux/services/arm_linux_syscalls.h
@@ -0,0 +1,1409 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's calls.S.
+#ifndef SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_
+
+#if !defined(__arm__) || !defined(__ARM_EABI__)
+#error "Including header on wrong architecture"
+#endif
+
+// __NR_SYSCALL_BASE, __ARM_NR_BASE are defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+
+// This syscall list has holes, because ARM EABI makes some syscalls obsolete.
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_SYSCALL_BASE+0)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_SYSCALL_BASE+1)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_SYSCALL_BASE+2)
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_SYSCALL_BASE+3)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_SYSCALL_BASE+4)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_SYSCALL_BASE+5)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_SYSCALL_BASE+6)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_SYSCALL_BASE+8)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_SYSCALL_BASE+9)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_SYSCALL_BASE+10)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_SYSCALL_BASE+11)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_SYSCALL_BASE+12)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_SYSCALL_BASE+14)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_SYSCALL_BASE+15)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_SYSCALL_BASE+16)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_SYSCALL_BASE+19)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_SYSCALL_BASE+20)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_SYSCALL_BASE+21)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_SYSCALL_BASE+23)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_SYSCALL_BASE+24)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_SYSCALL_BASE+26)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_SYSCALL_BASE+29)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_SYSCALL_BASE+33)
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice (__NR_SYSCALL_BASE+34)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_SYSCALL_BASE+36)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_SYSCALL_BASE+37)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_SYSCALL_BASE+38)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_SYSCALL_BASE+39)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_SYSCALL_BASE+40)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_SYSCALL_BASE+41)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_SYSCALL_BASE+42)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_SYSCALL_BASE+43)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_SYSCALL_BASE+45)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_SYSCALL_BASE+46)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_SYSCALL_BASE+47)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_SYSCALL_BASE+49)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_SYSCALL_BASE+50)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_SYSCALL_BASE+51)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_SYSCALL_BASE+52)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_SYSCALL_BASE+54)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_SYSCALL_BASE+55)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_SYSCALL_BASE+57)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_SYSCALL_BASE+60)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_SYSCALL_BASE+61)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_SYSCALL_BASE+62)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_SYSCALL_BASE+63)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_SYSCALL_BASE+64)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_SYSCALL_BASE+65)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_SYSCALL_BASE+66)
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction (__NR_SYSCALL_BASE+67)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_SYSCALL_BASE+70)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_SYSCALL_BASE+71)
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend (__NR_SYSCALL_BASE+72)
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending (__NR_SYSCALL_BASE+73)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_SYSCALL_BASE+74)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_SYSCALL_BASE+75)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_SYSCALL_BASE+77)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_SYSCALL_BASE+78)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_SYSCALL_BASE+79)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_SYSCALL_BASE+80)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_SYSCALL_BASE+81)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_SYSCALL_BASE+83)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_SYSCALL_BASE+85)
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib (__NR_SYSCALL_BASE+86)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_SYSCALL_BASE+87)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_SYSCALL_BASE+88)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_SYSCALL_BASE+91)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_SYSCALL_BASE+92)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_SYSCALL_BASE+93)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_SYSCALL_BASE+94)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_SYSCALL_BASE+95)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_SYSCALL_BASE+96)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_SYSCALL_BASE+97)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_SYSCALL_BASE+99)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_SYSCALL_BASE+100)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_SYSCALL_BASE+103)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_SYSCALL_BASE+104)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_SYSCALL_BASE+105)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_SYSCALL_BASE+106)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_SYSCALL_BASE+107)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_SYSCALL_BASE+108)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_SYSCALL_BASE+111)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_SYSCALL_BASE+114)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_SYSCALL_BASE+115)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_SYSCALL_BASE+116)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_SYSCALL_BASE+118)
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn (__NR_SYSCALL_BASE+119)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_SYSCALL_BASE+120)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_SYSCALL_BASE+121)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_SYSCALL_BASE+122)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_SYSCALL_BASE+124)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_SYSCALL_BASE+125)
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_SYSCALL_BASE+128)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_SYSCALL_BASE+129)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_SYSCALL_BASE+131)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_SYSCALL_BASE+132)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_SYSCALL_BASE+133)
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush (__NR_SYSCALL_BASE+134)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_SYSCALL_BASE+135)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_SYSCALL_BASE+136)
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_SYSCALL_BASE+138)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_SYSCALL_BASE+139)
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek (__NR_SYSCALL_BASE+140)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_SYSCALL_BASE+141)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_SYSCALL_BASE+142)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_SYSCALL_BASE+143)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_SYSCALL_BASE+144)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_SYSCALL_BASE+145)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_SYSCALL_BASE+146)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_SYSCALL_BASE+147)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_SYSCALL_BASE+148)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_SYSCALL_BASE+149)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_SYSCALL_BASE+150)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_SYSCALL_BASE+151)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_SYSCALL_BASE+152)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_SYSCALL_BASE+153)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_SYSCALL_BASE+154)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_SYSCALL_BASE+155)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_SYSCALL_BASE+158)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_SYSCALL_BASE+162)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_SYSCALL_BASE+163)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_SYSCALL_BASE+164)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_SYSCALL_BASE+165)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_SYSCALL_BASE+168)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_SYSCALL_BASE+170)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_SYSCALL_BASE+171)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_SYSCALL_BASE+172)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_SYSCALL_BASE+180)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_SYSCALL_BASE+181)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_SYSCALL_BASE+182)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_SYSCALL_BASE+183)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_SYSCALL_BASE+184)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_SYSCALL_BASE+185)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_SYSCALL_BASE+186)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_SYSCALL_BASE+187)
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork (__NR_SYSCALL_BASE+190)
+#endif
+
+#if !defined(__NR_ugetrlimit)
+#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191)
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 (__NR_SYSCALL_BASE+193)
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194)
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 (__NR_SYSCALL_BASE+195)
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 (__NR_SYSCALL_BASE+196)
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 (__NR_SYSCALL_BASE+197)
+#endif
+
+#if !defined(__NR_lchown32)
+#define __NR_lchown32 (__NR_SYSCALL_BASE+198)
+#endif
+
+#if !defined(__NR_getuid32)
+#define __NR_getuid32 (__NR_SYSCALL_BASE+199)
+#endif
+
+#if !defined(__NR_getgid32)
+#define __NR_getgid32 (__NR_SYSCALL_BASE+200)
+#endif
+
+#if !defined(__NR_geteuid32)
+#define __NR_geteuid32 (__NR_SYSCALL_BASE+201)
+#endif
+
+#if !defined(__NR_getegid32)
+#define __NR_getegid32 (__NR_SYSCALL_BASE+202)
+#endif
+
+#if !defined(__NR_setreuid32)
+#define __NR_setreuid32 (__NR_SYSCALL_BASE+203)
+#endif
+
+#if !defined(__NR_setregid32)
+#define __NR_setregid32 (__NR_SYSCALL_BASE+204)
+#endif
+
+#if !defined(__NR_getgroups32)
+#define __NR_getgroups32 (__NR_SYSCALL_BASE+205)
+#endif
+
+#if !defined(__NR_setgroups32)
+#define __NR_setgroups32 (__NR_SYSCALL_BASE+206)
+#endif
+
+#if !defined(__NR_fchown32)
+#define __NR_fchown32 (__NR_SYSCALL_BASE+207)
+#endif
+
+#if !defined(__NR_setresuid32)
+#define __NR_setresuid32 (__NR_SYSCALL_BASE+208)
+#endif
+
+#if !defined(__NR_getresuid32)
+#define __NR_getresuid32 (__NR_SYSCALL_BASE+209)
+#endif
+
+#if !defined(__NR_setresgid32)
+#define __NR_setresgid32 (__NR_SYSCALL_BASE+210)
+#endif
+
+#if !defined(__NR_getresgid32)
+#define __NR_getresgid32 (__NR_SYSCALL_BASE+211)
+#endif
+
+#if !defined(__NR_chown32)
+#define __NR_chown32 (__NR_SYSCALL_BASE+212)
+#endif
+
+#if !defined(__NR_setuid32)
+#define __NR_setuid32 (__NR_SYSCALL_BASE+213)
+#endif
+
+#if !defined(__NR_setgid32)
+#define __NR_setgid32 (__NR_SYSCALL_BASE+214)
+#endif
+
+#if !defined(__NR_setfsuid32)
+#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215)
+#endif
+
+#if !defined(__NR_setfsgid32)
+#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_SYSCALL_BASE+217)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_SYSCALL_BASE+218)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_SYSCALL_BASE+219)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_SYSCALL_BASE+220)
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 (__NR_SYSCALL_BASE+221)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_SYSCALL_BASE+224)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_SYSCALL_BASE+225)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_SYSCALL_BASE+226)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_SYSCALL_BASE+229)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_SYSCALL_BASE+231)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_SYSCALL_BASE+232)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_SYSCALL_BASE+233)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_SYSCALL_BASE+234)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_SYSCALL_BASE+235)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_SYSCALL_BASE+238)
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_SYSCALL_BASE+240)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_SYSCALL_BASE+243)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_SYSCALL_BASE+244)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_SYSCALL_BASE+245)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_SYSCALL_BASE+246)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_SYSCALL_BASE+247)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_SYSCALL_BASE+248)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_SYSCALL_BASE+250)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_SYSCALL_BASE+252)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_SYSCALL_BASE+256)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_SYSCALL_BASE+257)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_SYSCALL_BASE+258)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_SYSCALL_BASE+259)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_SYSCALL_BASE+261)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_SYSCALL_BASE+262)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_SYSCALL_BASE+263)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_SYSCALL_BASE+264)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265)
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 (__NR_SYSCALL_BASE+266)
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_SYSCALL_BASE+268)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_SYSCALL_BASE+269)
+#endif
+
+#if !defined(__NR_arm_fadvise64_64)
+#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270)
+#endif
+
+#if !defined(__NR_pciconfig_iobase)
+#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271)
+#endif
+
+#if !defined(__NR_pciconfig_read)
+#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272)
+#endif
+
+#if !defined(__NR_pciconfig_write)
+#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_SYSCALL_BASE+274)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_SYSCALL_BASE+275)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_SYSCALL_BASE+278)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_SYSCALL_BASE+280)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_SYSCALL_BASE+281)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_SYSCALL_BASE+282)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_SYSCALL_BASE+283)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_SYSCALL_BASE+284)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_SYSCALL_BASE+285)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_SYSCALL_BASE+286)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_SYSCALL_BASE+287)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_SYSCALL_BASE+288)
+#endif
+
+#if !defined(__NR_send)
+#define __NR_send (__NR_SYSCALL_BASE+289)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_SYSCALL_BASE+290)
+#endif
+
+#if !defined(__NR_recv)
+#define __NR_recv (__NR_SYSCALL_BASE+291)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_SYSCALL_BASE+292)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_SYSCALL_BASE+293)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_SYSCALL_BASE+294)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_SYSCALL_BASE+295)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_SYSCALL_BASE+296)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_SYSCALL_BASE+297)
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop (__NR_SYSCALL_BASE+298)
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget (__NR_SYSCALL_BASE+299)
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl (__NR_SYSCALL_BASE+300)
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd (__NR_SYSCALL_BASE+301)
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv (__NR_SYSCALL_BASE+302)
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget (__NR_SYSCALL_BASE+303)
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl (__NR_SYSCALL_BASE+304)
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat (__NR_SYSCALL_BASE+305)
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt (__NR_SYSCALL_BASE+306)
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget (__NR_SYSCALL_BASE+307)
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl (__NR_SYSCALL_BASE+308)
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_SYSCALL_BASE+309)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_SYSCALL_BASE+310)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_SYSCALL_BASE+311)
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop (__NR_SYSCALL_BASE+312)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_SYSCALL_BASE+313)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_SYSCALL_BASE+314)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_SYSCALL_BASE+315)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_SYSCALL_BASE+316)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_SYSCALL_BASE+319)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_SYSCALL_BASE+322)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_SYSCALL_BASE+323)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_SYSCALL_BASE+324)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_SYSCALL_BASE+325)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_SYSCALL_BASE+326)
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 (__NR_SYSCALL_BASE+327)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_SYSCALL_BASE+328)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_SYSCALL_BASE+329)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_SYSCALL_BASE+330)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_SYSCALL_BASE+331)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_SYSCALL_BASE+334)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_SYSCALL_BASE+336)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_SYSCALL_BASE+337)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_SYSCALL_BASE+340)
+#endif
+
+#if !defined(__NR_arm_sync_file_range)
+#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_SYSCALL_BASE+342)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_SYSCALL_BASE+344)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_SYSCALL_BASE+345)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_SYSCALL_BASE+348)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_SYSCALL_BASE+349)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate (__NR_SYSCALL_BASE+352)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_SYSCALL_BASE+355)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_SYSCALL_BASE+356)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_SYSCALL_BASE+358)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_SYSCALL_BASE+361)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_SYSCALL_BASE+362)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_SYSCALL_BASE+366)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_SYSCALL_BASE+367)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_SYSCALL_BASE+369)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_SYSCALL_BASE+372)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_SYSCALL_BASE+373)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_SYSCALL_BASE+374)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_SYSCALL_BASE+375)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_SYSCALL_BASE+378)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_SYSCALL_BASE+379)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_SYSCALL_BASE+380)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_SYSCALL_BASE+381)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_SYSCALL_BASE+382)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_SYSCALL_BASE+383)
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom (__NR_SYSCALL_BASE+384)
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create (__NR_SYSCALL_BASE+385)
+#endif
+
+// ARM private syscalls.
+#if !defined(__ARM_NR_breakpoint)
+#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
+#endif
+
+#if !defined(__ARM_NR_cacheflush)
+#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
+#endif
+
+#if !defined(__ARM_NR_usr26)
+#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
+#endif
+
+#if !defined(__ARM_NR_usr32)
+#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
+#endif
+
+#if !defined(__ARM_NR_set_tls)
+#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
+#endif
+
+// ARM kernel private syscall.
+#if !defined(__ARM_NR_cmpxchg)
+#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/services/broker_process.cc b/sandbox/linux/services/broker_process.cc
new file mode 100644
index 0000000..ef916f2
--- /dev/null
+++ b/sandbox/linux/services/broker_process.cc
@@ -0,0 +1,545 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/broker_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/process_metrics.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
+#define MSG_CMSG_CLOEXEC 0x40000000
+#endif
+
+namespace {
+
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+// A little open(2) wrapper to handle some oddities for us. In the general case
+// make a direct system call since we want to keep in control of the broker
+// process' system calls profile to be able to loosely sandbox it.
+int sys_open(const char* pathname, int flags) {
+ // Always pass a defined |mode| in case flags mistakenly contains O_CREAT.
+ const int mode = 0;
+ if (IsRunningOnValgrind()) {
+ // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
+ return open(pathname, flags, mode);
+ } else {
+ return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
+ }
+}
+
+static const size_t kMaxMessageLength = 4096;
+
+// Some flags are local to the current process and cannot be sent over a Unix
+// socket. They need special treatment from the client.
+// O_CLOEXEC is tricky because in theory another thread could call execve()
+// before special treatment is made on the client, so a client needs to call
+// recvmsg(2) with MSG_CMSG_CLOEXEC.
+// To make things worse, there are two CLOEXEC related flags, FD_CLOEXEC (see
+// F_GETFD in fcntl(2)) and O_CLOEXEC (see F_GETFL in fcntl(2)). O_CLOEXEC
+// doesn't affect the semantics on execve(), it's merely a note that the
+// descriptor was originally opened with O_CLOEXEC as a flag. And it is sent
+// over unix sockets just fine, so a receiver that would (incorrectly) look at
+// O_CLOEXEC instead of FD_CLOEXEC may be tricked in thinking that the file
+// descriptor will or won't be closed on execve().
+static const int kCurrentProcessOpenFlagsMask = O_CLOEXEC;
+
+// Check whether |requested_filename| is in |allowed_file_names|.
+// See GetFileNameIfAllowedToOpen() for an explanation of |file_to_open|.
+// async signal safe if |file_to_open| is NULL.
+// TODO(jln): assert signal safety.
+bool GetFileNameInWhitelist(const std::vector<std::string>& allowed_file_names,
+ const char* requested_filename,
+ const char** file_to_open) {
+ if (file_to_open && *file_to_open) {
+ // Make sure that callers never pass a non-empty string. In case callers
+ // wrongly forget to check the return value and look at the string
+ // instead, this could catch bugs.
+ RAW_LOG(FATAL, "*file_to_open should be NULL");
+ return false;
+ }
+
+ // Look for |requested_filename| in |allowed_file_names|.
+ // We don't use ::find() because it takes a std::string and
+ // the conversion allocates memory.
+ std::vector<std::string>::const_iterator it;
+ for (it = allowed_file_names.begin(); it != allowed_file_names.end(); it++) {
+ if (strcmp(requested_filename, it->c_str()) == 0) {
+ if (file_to_open)
+ *file_to_open = it->c_str();
+ return true;
+ }
+ }
+ return false;
+}
+
+// We maintain a list of flags that have been reviewed for "sanity" and that
+// we're ok to allow in the broker.
+// I.e. here is where we wouldn't add O_RESET_FILE_SYSTEM.
+bool IsAllowedOpenFlags(int flags) {
+ // First, check the access mode.
+ const int access_mode = flags & O_ACCMODE;
+ if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
+ access_mode != O_RDWR) {
+ return false;
+ }
+
+ // We only support a 2-parameters open, so we forbid O_CREAT.
+ if (flags & O_CREAT) {
+ return false;
+ }
+
+ // Some flags affect the behavior of the current process. We don't support
+ // them and don't allow them for now.
+ if (flags & kCurrentProcessOpenFlagsMask)
+ return false;
+
+ // Now check that all the flags are known to us.
+ const int creation_and_status_flags = flags & ~O_ACCMODE;
+
+ const int known_flags =
+ O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT |
+ O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME | O_NOCTTY |
+ O_NOFOLLOW | O_NONBLOCK | O_NDELAY | O_SYNC | O_TRUNC;
+
+ const int unknown_flags = ~known_flags;
+ const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
+ return !has_unknown_flags;
+}
+
+} // namespace
+
+namespace sandbox {
+
+BrokerProcess::BrokerProcess(int denied_errno,
+ const std::vector<std::string>& allowed_r_files,
+ const std::vector<std::string>& allowed_w_files,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : denied_errno_(denied_errno),
+ initialized_(false),
+ is_child_(false),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests),
+ broker_pid_(-1),
+ allowed_r_files_(allowed_r_files),
+ allowed_w_files_(allowed_w_files),
+ ipc_socketpair_(-1) {
+}
+
+BrokerProcess::~BrokerProcess() {
+ if (initialized_ && ipc_socketpair_ != -1) {
+ // Closing the socket should be enough to notify the child to die,
+ // unless it has been duplicated.
+ PCHECK(0 == IGNORE_EINTR(close(ipc_socketpair_)));
+ PCHECK(0 == kill(broker_pid_, SIGKILL));
+ siginfo_t process_info;
+ // Reap the child.
+ int ret = HANDLE_EINTR(waitid(P_PID, broker_pid_, &process_info, WEXITED));
+ PCHECK(0 == ret);
+ }
+}
+
+bool BrokerProcess::Init(
+ const base::Callback<bool(void)>& broker_process_init_callback) {
+ CHECK(!initialized_);
+ int socket_pair[2];
+ // Use SOCK_SEQPACKET, because we need to preserve message boundaries
+ // but we also want to be notified (recvmsg should return and not block)
+ // when the connection has been broken (one of the processes died).
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair)) {
+ LOG(ERROR) << "Failed to create socketpair";
+ return false;
+ }
+
+#if !defined(THREAD_SANITIZER)
+ DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+#endif
+ int child_pid = fork();
+ if (child_pid == -1) {
+ close(socket_pair[0]);
+ close(socket_pair[1]);
+ return false;
+ }
+ if (child_pid) {
+ // We are the parent and we have just forked our broker process.
+ close(socket_pair[0]);
+ // We should only be able to write to the IPC channel. We'll always send
+ // a new file descriptor to receive the reply on.
+ shutdown(socket_pair[1], SHUT_RD);
+ ipc_socketpair_ = socket_pair[1];
+ is_child_ = false;
+ broker_pid_ = child_pid;
+ initialized_ = true;
+ return true;
+ } else {
+ // We are the broker.
+ close(socket_pair[1]);
+ // We should only be able to read from this IPC channel. We will send our
+ // replies on a new file descriptor attached to the requests.
+ shutdown(socket_pair[0], SHUT_WR);
+ ipc_socketpair_ = socket_pair[0];
+ is_child_ = true;
+ CHECK(broker_process_init_callback.Run());
+ initialized_ = true;
+ for (;;) {
+ HandleRequest();
+ }
+ _exit(1);
+ }
+ NOTREACHED();
+}
+
+int BrokerProcess::Access(const char* pathname, int mode) const {
+ return PathAndFlagsSyscall(kCommandAccess, pathname, mode);
+}
+
+int BrokerProcess::Open(const char* pathname, int flags) const {
+ return PathAndFlagsSyscall(kCommandOpen, pathname, flags);
+}
+
+// Make a remote system call over IPC for syscalls that take a path and flags
+// as arguments, currently open() and access().
+// Will return -errno like a real system call.
+// This function needs to be async signal safe.
+int BrokerProcess::PathAndFlagsSyscall(enum IPCCommands syscall_type,
+ const char* pathname, int flags) const {
+ int recvmsg_flags = 0;
+ RAW_CHECK(initialized_); // async signal safe CHECK().
+ RAW_CHECK(syscall_type == kCommandOpen || syscall_type == kCommandAccess);
+ if (!pathname)
+ return -EFAULT;
+
+ // For this "remote system call" to work, we need to handle any flag that
+ // cannot be sent over a Unix socket in a special way.
+ // See the comments around kCurrentProcessOpenFlagsMask.
+ if (syscall_type == kCommandOpen && (flags & kCurrentProcessOpenFlagsMask)) {
+ // This implementation only knows about O_CLOEXEC, someone needs to look at
+ // this code if other flags are added.
+ RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
+ recvmsg_flags |= MSG_CMSG_CLOEXEC;
+ flags &= ~O_CLOEXEC;
+ }
+
+ // There is no point in forwarding a request that we know will be denied.
+ // Of course, the real security check needs to be on the other side of the
+ // IPC.
+ if (fast_check_in_client_) {
+ if (syscall_type == kCommandOpen &&
+ !GetFileNameIfAllowedToOpen(pathname, flags, NULL)) {
+ return -denied_errno_;
+ }
+ if (syscall_type == kCommandAccess &&
+ !GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
+ return -denied_errno_;
+ }
+ }
+
+ Pickle write_pickle;
+ write_pickle.WriteInt(syscall_type);
+ write_pickle.WriteString(pathname);
+ write_pickle.WriteInt(flags);
+ RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+ int returned_fd = -1;
+ uint8_t reply_buf[kMaxMessageLength];
+
+ // Send a request (in write_pickle) as well that will include a new
+ // temporary socketpair (created internally by SendRecvMsg()).
+ // Then read the reply on this new socketpair in reply_buf and put an
+ // eventual attached file descriptor in |returned_fd|.
+ ssize_t msg_len = UnixDomainSocket::SendRecvMsgWithFlags(ipc_socketpair_,
+ reply_buf,
+ sizeof(reply_buf),
+ recvmsg_flags,
+ &returned_fd,
+ write_pickle);
+ if (msg_len <= 0) {
+ if (!quiet_failures_for_tests_)
+ RAW_LOG(ERROR, "Could not make request to broker process");
+ return -ENOMEM;
+ }
+
+ Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ PickleIterator iter(read_pickle);
+ int return_value = -1;
+ // Now deserialize the return value and eventually return the file
+ // descriptor.
+ if (read_pickle.ReadInt(&iter, &return_value)) {
+ switch (syscall_type) {
+ case kCommandAccess:
+ // We should never have a fd to return.
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ case kCommandOpen:
+ if (return_value < 0) {
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ } else {
+ // We have a real file descriptor to return.
+ RAW_CHECK(returned_fd >= 0);
+ return returned_fd;
+ }
+ default:
+ RAW_LOG(ERROR, "Unsupported command");
+ return -ENOSYS;
+ }
+ } else {
+ RAW_LOG(ERROR, "Could not read pickle");
+ NOTREACHED();
+ return -ENOMEM;
+ }
+}
+
+// Handle a request on the IPC channel ipc_socketpair_.
+// A request should have a file descriptor attached on which we will reply and
+// that we will then close.
+// A request should start with an int that will be used as the command type.
+bool BrokerProcess::HandleRequest() const {
+ ScopedVector<base::ScopedFD> fds;
+ char buf[kMaxMessageLength];
+ errno = 0;
+ const ssize_t msg_len = UnixDomainSocket::RecvMsg(ipc_socketpair_, buf,
+ sizeof(buf), &fds);
+
+ if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
+ // EOF from our parent, or our parent died, we should die.
+ _exit(0);
+ }
+
+ // The parent should send exactly one file descriptor, on which we
+ // will write the reply.
+ // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'.
+ if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) {
+ PLOG(ERROR) << "Error reading message from the client";
+ return false;
+ }
+
+ base::ScopedFD temporary_ipc(fds[0]->Pass());
+
+ Pickle pickle(buf, msg_len);
+ PickleIterator iter(pickle);
+ int command_type;
+ if (pickle.ReadInt(&iter, &command_type)) {
+ bool r = false;
+ // Go through all the possible IPC messages.
+ switch (command_type) {
+ case kCommandAccess:
+ case kCommandOpen:
+ // We reply on the file descriptor sent to us via the IPC channel.
+ r = HandleRemoteCommand(static_cast<IPCCommands>(command_type),
+ temporary_ipc.get(), pickle, iter);
+ break;
+ default:
+ NOTREACHED();
+ r = false;
+ break;
+ }
+ return r;
+ }
+
+ LOG(ERROR) << "Error parsing IPC request";
+ return false;
+}
+
+// Handle a |command_type| request contained in |read_pickle| and send the reply
+// on |reply_ipc|.
+// Currently kCommandOpen and kCommandAccess are supported.
+bool BrokerProcess::HandleRemoteCommand(IPCCommands command_type, int reply_ipc,
+ const Pickle& read_pickle,
+ PickleIterator iter) const {
+ // Currently all commands have two arguments: filename and flags.
+ std::string requested_filename;
+ int flags = 0;
+ if (!read_pickle.ReadString(&iter, &requested_filename) ||
+ !read_pickle.ReadInt(&iter, &flags)) {
+ return -1;
+ }
+
+ Pickle write_pickle;
+ std::vector<int> opened_files;
+
+ switch (command_type) {
+ case kCommandAccess:
+ AccessFileForIPC(requested_filename, flags, &write_pickle);
+ break;
+ case kCommandOpen:
+ OpenFileForIPC(requested_filename, flags, &write_pickle, &opened_files);
+ break;
+ default:
+ LOG(ERROR) << "Invalid IPC command";
+ break;
+ }
+
+ CHECK_LE(write_pickle.size(), kMaxMessageLength);
+ ssize_t sent = UnixDomainSocket::SendMsg(reply_ipc, write_pickle.data(),
+ write_pickle.size(), opened_files);
+
+ // Close anything we have opened in this process.
+ for (std::vector<int>::iterator it = opened_files.begin();
+ it != opened_files.end(); ++it) {
+ int ret = IGNORE_EINTR(close(*it));
+ DCHECK(!ret) << "Could not close file descriptor";
+ }
+
+ if (sent <= 0) {
+ LOG(ERROR) << "Could not send IPC reply";
+ return false;
+ }
+ return true;
+}
+
+// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
+// policy. Write the syscall return value (-errno) to |write_pickle|.
+void BrokerProcess::AccessFileForIPC(const std::string& requested_filename,
+ int mode, Pickle* write_pickle) const {
+ DCHECK(write_pickle);
+ const char* file_to_access = NULL;
+ const bool safe_to_access_file = GetFileNameIfAllowedToAccess(
+ requested_filename.c_str(), mode, &file_to_access);
+
+ if (safe_to_access_file) {
+ CHECK(file_to_access);
+ int access_ret = access(file_to_access, mode);
+ int access_errno = errno;
+ if (!access_ret)
+ write_pickle->WriteInt(0);
+ else
+ write_pickle->WriteInt(-access_errno);
+ } else {
+ write_pickle->WriteInt(-denied_errno_);
+ }
+}
+
+// Open |requested_filename| with |flags| if allowed by our policy.
+// Write the syscall return value (-errno) to |write_pickle| and append
+// a file descriptor to |opened_files| if relevant.
+void BrokerProcess::OpenFileForIPC(const std::string& requested_filename,
+ int flags, Pickle* write_pickle,
+ std::vector<int>* opened_files) const {
+ DCHECK(write_pickle);
+ DCHECK(opened_files);
+ const char* file_to_open = NULL;
+ const bool safe_to_open_file = GetFileNameIfAllowedToOpen(
+ requested_filename.c_str(), flags, &file_to_open);
+
+ if (safe_to_open_file) {
+ CHECK(file_to_open);
+ int opened_fd = sys_open(file_to_open, flags);
+ if (opened_fd < 0) {
+ write_pickle->WriteInt(-errno);
+ } else {
+ // Success.
+ opened_files->push_back(opened_fd);
+ write_pickle->WriteInt(0);
+ }
+ } else {
+ write_pickle->WriteInt(-denied_errno_);
+ }
+}
+
+
+// Check if calling access() should be allowed on |requested_filename| with
+// mode |requested_mode|.
+// Note: access() being a system call to check permissions, this can get a bit
+// confusing. We're checking if calling access() should even be allowed with
+// the same policy we would use for open().
+// If |file_to_access| is not NULL, we will return the matching pointer from
+// the whitelist. For paranoia a caller should then use |file_to_access|. See
+// GetFileNameIfAllowedToOpen() fore more explanation.
+// return true if calling access() on this file should be allowed, false
+// otherwise.
+// Async signal safe if and only if |file_to_access| is NULL.
+bool BrokerProcess::GetFileNameIfAllowedToAccess(const char* requested_filename,
+ int requested_mode, const char** file_to_access) const {
+ // First, check if |requested_mode| is existence, ability to read or ability
+ // to write. We do not support X_OK.
+ if (requested_mode != F_OK &&
+ requested_mode & ~(R_OK | W_OK)) {
+ return false;
+ }
+ switch (requested_mode) {
+ case F_OK:
+ // We allow to check for file existence if we can either read or write.
+ return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+ file_to_access) ||
+ GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_access);
+ case R_OK:
+ return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+ file_to_access);
+ case W_OK:
+ return GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_access);
+ case R_OK | W_OK:
+ {
+ bool allowed_for_read_and_write =
+ GetFileNameInWhitelist(allowed_r_files_, requested_filename, NULL) &&
+ GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_access);
+ return allowed_for_read_and_write;
+ }
+ default:
+ return false;
+ }
+}
+
+// Check if |requested_filename| can be opened with flags |requested_flags|.
+// If |file_to_open| is not NULL, we will return the matching pointer from the
+// whitelist. For paranoia, a caller should then use |file_to_open| rather
+// than |requested_filename|, so that it never attempts to open an
+// attacker-controlled file name, even if an attacker managed to fool the
+// string comparison mechanism.
+// Return true if opening should be allowed, false otherwise.
+// Async signal safe if and only if |file_to_open| is NULL.
+bool BrokerProcess::GetFileNameIfAllowedToOpen(const char* requested_filename,
+ int requested_flags, const char** file_to_open) const {
+ if (!IsAllowedOpenFlags(requested_flags)) {
+ return false;
+ }
+ switch (requested_flags & O_ACCMODE) {
+ case O_RDONLY:
+ return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+ file_to_open);
+ case O_WRONLY:
+ return GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_open);
+ case O_RDWR:
+ {
+ bool allowed_for_read_and_write =
+ GetFileNameInWhitelist(allowed_r_files_, requested_filename, NULL) &&
+ GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_open);
+ return allowed_for_read_and_write;
+ }
+ default:
+ return false;
+ }
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/broker_process.h b/sandbox/linux/services/broker_process.h
new file mode 100644
index 0000000..ddde42e
--- /dev/null
+++ b/sandbox/linux/services/broker_process.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/pickle.h"
+#include "base/process/process.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Create a new "broker" process to which we can send requests via an IPC
+// channel.
+// This is a low level IPC mechanism that is suitable to be called from a
+// signal handler.
+// A process would typically create a broker process before entering
+// sandboxing.
+// 1. BrokerProcess open_broker(read_whitelist, write_whitelist);
+// 2. CHECK(open_broker.Init(NULL));
+// 3. Enable sandbox.
+// 4. Use open_broker.Open() to open files.
+class SANDBOX_EXPORT BrokerProcess {
+ public:
+ // |denied_errno| is the error code returned when methods such as Open()
+ // or Access() are invoked on a file which is not in the whitelist. EACCESS
+ // would be a typical value.
+ // |allowed_r_files| and |allowed_w_files| are white lists of files that can
+ // be opened later via the Open() API, respectively for reading and writing.
+ // A file available read-write should be listed in both.
+ // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
+ // unit tests, don't use it.
+ explicit BrokerProcess(int denied_errno,
+ const std::vector<std::string>& allowed_r_files,
+ const std::vector<std::string>& allowed_w_files,
+ bool fast_check_in_client = true,
+ bool quiet_failures_for_tests = false);
+ ~BrokerProcess();
+ // Will initialize the broker process. There should be no threads at this
+ // point, since we need to fork().
+ // broker_process_init_callback will be called in the new broker process,
+ // after fork() returns.
+ bool Init(const base::Callback<bool(void)>& broker_process_init_callback);
+
+ // Can be used in place of access(). Will be async signal safe.
+ // X_OK will always return an error in practice since the broker process
+ // doesn't support execute permissions.
+ // It's similar to the access() system call and will return -errno on errors.
+ int Access(const char* pathname, int mode) const;
+ // Can be used in place of open(). Will be async signal safe.
+ // The implementation only supports certain white listed flags and will
+ // return -EPERM on other flags.
+ // It's similar to the open() system call and will return -errno on errors.
+ int Open(const char* pathname, int flags) const;
+
+ int broker_pid() const { return broker_pid_; }
+
+ private:
+ enum IPCCommands {
+ kCommandInvalid = 0,
+ kCommandOpen,
+ kCommandAccess,
+ };
+ int PathAndFlagsSyscall(enum IPCCommands command_type,
+ const char* pathname,
+ int flags) const;
+ bool HandleRequest() const;
+ bool HandleRemoteCommand(IPCCommands command_type,
+ int reply_ipc,
+ const Pickle& read_pickle,
+ PickleIterator iter) const;
+
+ void AccessFileForIPC(const std::string& requested_filename,
+ int mode,
+ Pickle* write_pickle) const;
+ void OpenFileForIPC(const std::string& requested_filename,
+ int flags,
+ Pickle* write_pickle,
+ std::vector<int>* opened_files) const;
+ bool GetFileNameIfAllowedToAccess(const char*, int, const char**) const;
+ bool GetFileNameIfAllowedToOpen(const char*, int, const char**) const;
+ const int denied_errno_;
+ bool initialized_; // Whether we've been through Init() yet.
+ bool is_child_; // Whether we're the child (broker process).
+ bool fast_check_in_client_; // Whether to forward a request that we know
+ // will be denied to the broker.
+ bool quiet_failures_for_tests_; // Disable certain error message when
+ // testing for failures.
+ pid_t broker_pid_; // The PID of the broker (child).
+ const std::vector<std::string> allowed_r_files_; // Files allowed for read.
+ const std::vector<std::string> allowed_w_files_; // Files allowed for write.
+ int ipc_socketpair_; // Our communication channel to parent or child.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerProcess);
+
+ friend class BrokerProcessTestHelper;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
diff --git a/sandbox/linux/services/broker_process_unittest.cc b/sandbox/linux/services/broker_process_unittest.cc
new file mode 100644
index 0000000..87a7a31
--- /dev/null
+++ b/sandbox/linux/services/broker_process_unittest.cc
@@ -0,0 +1,477 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/broker_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+class BrokerProcessTestHelper {
+ public:
+ static int get_ipc_socketpair(const BrokerProcess* broker) {
+ return broker->ipc_socketpair_;
+ }
+};
+
+namespace {
+
+bool NoOpCallback() { return true; }
+
+} // namespace
+
+TEST(BrokerProcess, CreateAndDestroy) {
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back("/proc/cpuinfo");
+
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, read_whitelist, std::vector<std::string>()));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ // Destroy the broker and check it has exited properly.
+ open_broker.reset();
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+}
+
+TEST(BrokerProcess, TestOpenAccessNull) {
+ const std::vector<std::string> empty;
+ BrokerProcess open_broker(EPERM, empty, empty);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ int fd = open_broker.Open(NULL, O_RDONLY);
+ ASSERT_EQ(fd, -EFAULT);
+
+ int ret = open_broker.Access(NULL, F_OK);
+ ASSERT_EQ(ret, -EFAULT);
+}
+
+void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
+ const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1";
+ // We can't debug the init process, and shouldn't be able to access
+ // its auxv file.
+ const char kR_WhiteListedButDenied[] = "/proc/1/auxv";
+ const char kW_WhiteListed[] = "/proc/DOESNOTEXIST2";
+ const char kRW_WhiteListed[] = "/proc/DOESNOTEXIST3";
+ const char k_NotWhitelisted[] = "/proc/DOESNOTEXIST4";
+
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back(kR_WhiteListed);
+ read_whitelist.push_back(kR_WhiteListedButDenied);
+ read_whitelist.push_back(kRW_WhiteListed);
+
+ std::vector<std::string> write_whitelist;
+ write_whitelist.push_back(kW_WhiteListed);
+ write_whitelist.push_back(kRW_WhiteListed);
+
+ BrokerProcess open_broker(denied_errno,
+ read_whitelist,
+ write_whitelist,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+ fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kR_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kR_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ int ret = -1;
+ ret = open_broker.Access(kR_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ // Android sometimes runs tests as root.
+ // This part of the test requires a process that doesn't have
+ // CAP_DAC_OVERRIDE. We check against a root euid as a proxy for that.
+ if (geteuid()) {
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDONLY);
+ // The broker process will allow this, but the normal permission system
+ // won't.
+ ASSERT_EQ(fd, -EACCES);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, F_OK);
+ // The normal permission system will let us check that the file exists.
+ ASSERT_EQ(ret, 0);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK);
+ ASSERT_EQ(ret, -EACCES);
+ ret = open_broker.Access(kR_WhiteListedButDenied, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ }
+
+ fd = open_broker.Open(kW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kRW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ fd = open_broker.Open(k_NotWhitelisted, O_RDONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(k_NotWhitelisted, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(k_NotWhitelisted, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, F_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ // We have some extra sanity check for clearly wrong values.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY | O_WRONLY | O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+
+ // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
+ // is denied.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR | O_CREAT);
+ ASSERT_EQ(fd, -denied_errno);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenFilePermsWithClientCheck) {
+ TestOpenFilePerms(true /* fast_check_in_client */, EPERM);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) {
+ TestOpenFilePerms(false /* fast_check_in_client */, EPERM);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+// Run the same twice again, but with ENOENT instead of EPERM.
+TEST(BrokerProcess, OpenFilePermsWithClientCheckNoEnt) {
+ TestOpenFilePerms(true /* fast_check_in_client */, ENOENT);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheckNoEnt) {
+ TestOpenFilePerms(false /* fast_check_in_client */, ENOENT);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+void TestOpenCpuinfo(bool fast_check_in_client) {
+ const char kFileCpuInfo[] = "/proc/cpuinfo";
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back(kFileCpuInfo);
+
+ scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(
+ EPERM, read_whitelist, std::vector<std::string>(), fast_check_in_client));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+ fd = open_broker->Open(kFileCpuInfo, O_RDWR);
+ base::ScopedFD fd_closer(fd);
+ ASSERT_EQ(fd, -EPERM);
+
+ // Check we can read /proc/cpuinfo.
+ int can_access = open_broker->Access(kFileCpuInfo, R_OK);
+ ASSERT_EQ(can_access, 0);
+ can_access = open_broker->Access(kFileCpuInfo, W_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ // Check we can not write /proc/cpuinfo.
+
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ ASSERT_GE(cpuinfo_fd, 0);
+ char buf[3];
+ memset(buf, 0, sizeof(buf));
+ int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
+ ASSERT_GT(read_len1, 0);
+
+ // Open cpuinfo directly.
+ int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
+ ASSERT_GE(cpuinfo_fd2, 0);
+ char buf2[3];
+ memset(buf2, 1, sizeof(buf2));
+ int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
+ ASSERT_GT(read_len1, 0);
+
+ // The following is not guaranteed true, but will be in practice.
+ ASSERT_EQ(read_len1, read_len2);
+ // Compare the cpuinfo as returned by the broker with the one we opened
+ // ourselves.
+ ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ open_broker.reset();
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
+ TestOpenCpuinfo(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
+ TestOpenCpuinfo(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenFileRW) {
+ ScopedTemporaryFile tempfile;
+ const char* tempfile_name = tempfile.full_file_name();
+
+ std::vector<std::string> whitelist;
+ whitelist.push_back(tempfile_name);
+
+ BrokerProcess open_broker(EPERM, whitelist, whitelist);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ // Check we can access that file with read or write.
+ int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
+ ASSERT_EQ(can_access, 0);
+
+ int tempfile2 = -1;
+ tempfile2 = open_broker.Open(tempfile_name, O_RDWR);
+ ASSERT_GE(tempfile2, 0);
+
+ // Write to the descriptor opened by the broker.
+ char test_text[] = "TESTTESTTEST";
+ ssize_t len = write(tempfile2, test_text, sizeof(test_text));
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+
+ // Read back from the original file descriptor what we wrote through
+ // the descriptor provided by the broker.
+ char buf[1024];
+ len = read(tempfile.fd(), buf, sizeof(buf));
+
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+ ASSERT_EQ(memcmp(test_text, buf, sizeof(test_text)), 0);
+
+ ASSERT_EQ(close(tempfile2), 0);
+}
+
+// SANDBOX_TEST because the process could die with a SIGPIPE
+// and we want this to happen in a subprocess.
+SANDBOX_TEST(BrokerProcess, BrokerDied) {
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back("/proc/cpuinfo");
+
+ BrokerProcess open_broker(EPERM,
+ read_whitelist,
+ std::vector<std::string>(),
+ true /* fast_check_in_client */,
+ true /* quiet_failures_for_tests */);
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+ const pid_t broker_pid = open_broker.broker_pid();
+ SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
+
+ // Now we check that the broker has been signaled, but do not reap it.
+ siginfo_t process_info;
+ SANDBOX_ASSERT(HANDLE_EINTR(waitid(
+ P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
+ 0);
+ SANDBOX_ASSERT(broker_pid == process_info.si_pid);
+ SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
+ SANDBOX_ASSERT(SIGKILL == process_info.si_status);
+
+ // Check that doing Open with a dead broker won't SIGPIPE us.
+ SANDBOX_ASSERT(open_broker.Open("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
+ SANDBOX_ASSERT(open_broker.Access("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
+}
+
+void TestOpenComplexFlags(bool fast_check_in_client) {
+ const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<std::string> whitelist;
+ whitelist.push_back(kCpuInfo);
+
+ BrokerProcess open_broker(EPERM,
+ whitelist,
+ whitelist,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
+ int fd = -1;
+ int ret = 0;
+ fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ // The descriptor shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
+ ASSERT_EQ(0, ret & (O_CLOEXEC | O_NONBLOCK));
+ ASSERT_EQ(0, close(fd));
+
+ fd = open_broker.Open(kCpuInfo, O_RDONLY | O_CLOEXEC);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFD);
+ ASSERT_NE(-1, ret);
+ // Important: use F_GETFD, not F_GETFL. The O_CLOEXEC flag in F_GETFL
+ // is actually not used by the kernel.
+ ASSERT_TRUE(FD_CLOEXEC & ret);
+ ASSERT_EQ(0, close(fd));
+
+ fd = open_broker.Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ ASSERT_TRUE(O_NONBLOCK & ret);
+ ASSERT_EQ(0, close(fd));
+}
+
+TEST(BrokerProcess, OpenComplexFlagsWithClientCheck) {
+ TestOpenComplexFlags(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenComplexFlagsNoClientCheck) {
+ TestOpenComplexFlags(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+// We need to allow noise because the broker will log when it receives our
+// bogus IPCs.
+SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, RecvMsgDescriptorLeak) {
+ // Android creates a socket on first use of the LOG call.
+ // We need to ensure this socket is open before we
+ // begin the test.
+ LOG(INFO) << "Ensure Android LOG socket is allocated";
+
+ // Find the four lowest available file descriptors.
+ int available_fds[4];
+ SANDBOX_ASSERT(0 == pipe(available_fds));
+ SANDBOX_ASSERT(0 == pipe(available_fds + 2));
+
+ // Save one FD to send to the broker later, and close the others.
+ base::ScopedFD message_fd(available_fds[0]);
+ for (size_t i = 1; i < arraysize(available_fds); i++) {
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(available_fds[i])));
+ }
+
+ // Lower our file descriptor limit to just allow three more file descriptors
+ // to be allocated. (N.B., RLIMIT_NOFILE doesn't limit the number of file
+ // descriptors a process can have: it only limits the highest value that can
+ // be assigned to newly-created descriptors allocated by the process.)
+ const rlim_t fd_limit =
+ 1 + *std::max_element(available_fds,
+ available_fds + arraysize(available_fds));
+
+ // Valgrind doesn't allow changing the hard descriptor limit, so we only
+ // change the soft descriptor limit here.
+ struct rlimit rlim;
+ SANDBOX_ASSERT(0 == getrlimit(RLIMIT_NOFILE, &rlim));
+ SANDBOX_ASSERT(fd_limit <= rlim.rlim_cur);
+ rlim.rlim_cur = fd_limit;
+ SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
+
+ static const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back(kCpuInfo);
+
+ BrokerProcess open_broker(EPERM, read_whitelist, std::vector<std::string>());
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ const int ipc_fd = BrokerProcessTestHelper::get_ipc_socketpair(&open_broker);
+ SANDBOX_ASSERT(ipc_fd >= 0);
+
+ static const char kBogus[] = "not a pickle";
+ std::vector<int> fds;
+ fds.push_back(message_fd.get());
+
+ // The broker process should only have a couple spare file descriptors
+ // available, but for good measure we send it fd_limit bogus IPCs anyway.
+ for (rlim_t i = 0; i < fd_limit; ++i) {
+ SANDBOX_ASSERT(
+ UnixDomainSocket::SendMsg(ipc_fd, kBogus, sizeof(kBogus), fds));
+ }
+
+ const int fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ SANDBOX_ASSERT(fd >= 0);
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
new file mode 100644
index 0000000..96702b1
--- /dev/null
+++ b/sandbox/linux/services/credentials.cc
@@ -0,0 +1,347 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/credentials.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/template_util.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "base/threading/thread.h"
+
+namespace {
+
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+struct CapFreeDeleter {
+ inline void operator()(cap_t cap) const {
+ int ret = cap_free(cap);
+ CHECK_EQ(0, ret);
+ }
+};
+
+// Wrapper to manage libcap2's cap_t type.
+typedef scoped_ptr<typeof(*((cap_t)0)), CapFreeDeleter> ScopedCap;
+
+struct CapTextFreeDeleter {
+ inline void operator()(char* cap_text) const {
+ int ret = cap_free(cap_text);
+ CHECK_EQ(0, ret);
+ }
+};
+
+// Wrapper to manage the result from libcap2's cap_from_text().
+typedef scoped_ptr<char, CapTextFreeDeleter> ScopedCapText;
+
+struct FILECloser {
+ inline void operator()(FILE* f) const {
+ DCHECK(f);
+ PCHECK(0 == fclose(f));
+ }
+};
+
+// Don't use ScopedFILE in base since it doesn't check fclose().
+// TODO(jln): fix base/.
+typedef scoped_ptr<FILE, FILECloser> ScopedFILE;
+
+struct DIRCloser {
+ void operator()(DIR* d) const {
+ DCHECK(d);
+ PCHECK(0 == closedir(d));
+ }
+};
+
+typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
+
+COMPILE_ASSERT((base::is_same<uid_t, gid_t>::value), UidAndGidAreSameType);
+// generic_id_t can be used for either uid_t or gid_t.
+typedef uid_t generic_id_t;
+
+// Write a uid or gid mapping from |id| to |id| in |map_file|.
+bool WriteToIdMapFile(const char* map_file, generic_id_t id) {
+ ScopedFILE f(fopen(map_file, "w"));
+ PCHECK(f);
+ const uid_t inside_id = id;
+ const uid_t outside_id = id;
+ int num = fprintf(f.get(), "%d %d 1\n", inside_id, outside_id);
+ if (num < 0) return false;
+ // Manually call fflush() to catch permission failures.
+ int ret = fflush(f.get());
+ if (ret) {
+ VLOG(1) << "Could not write to id map file";
+ return false;
+ }
+ return true;
+}
+
+// Checks that the set of RES-uids and the set of RES-gids have
+// one element each and return that element in |resuid| and |resgid|
+// respectively. It's ok to pass NULL as one or both of the ids.
+bool GetRESIds(uid_t* resuid, gid_t* resgid) {
+ uid_t ruid, euid, suid;
+ gid_t rgid, egid, sgid;
+ PCHECK(getresuid(&ruid, &euid, &suid) == 0);
+ PCHECK(getresgid(&rgid, &egid, &sgid) == 0);
+ const bool uids_are_equal = (ruid == euid) && (ruid == suid);
+ const bool gids_are_equal = (rgid == egid) && (rgid == sgid);
+ if (!uids_are_equal || !gids_are_equal) return false;
+ if (resuid) *resuid = euid;
+ if (resgid) *resgid = egid;
+ return true;
+}
+
+// chroot() and chdir() to /proc/<tid>/fdinfo.
+void ChrootToThreadFdInfo(base::PlatformThreadId tid, bool* result) {
+ DCHECK(result);
+ *result = false;
+
+ COMPILE_ASSERT((base::is_same<base::PlatformThreadId, int>::value),
+ TidIsAnInt);
+ const std::string current_thread_fdinfo = "/proc/" +
+ base::IntToString(tid) + "/fdinfo/";
+
+ // Make extra sure that /proc/<tid>/fdinfo is unique to the thread.
+ CHECK(0 == unshare(CLONE_FILES));
+ int chroot_ret = chroot(current_thread_fdinfo.c_str());
+ if (chroot_ret) {
+ PLOG(ERROR) << "Could not chroot";
+ return;
+ }
+
+ // CWD is essentially an implicit file descriptor, so be careful to not leave
+ // it behind.
+ PCHECK(0 == chdir("/"));
+
+ *result = true;
+ return;
+}
+
+// chroot() to an empty dir that is "safe". To be safe, it must not contain
+// any subdirectory (chroot-ing there would allow a chroot escape) and it must
+// be impossible to create an empty directory there.
+// We achieve this by doing the following:
+// 1. We create a new thread, which will create a new /proc/<tid>/ directory
+// 2. We chroot to /proc/<tid>/fdinfo/
+// This is already "safe", since fdinfo/ does not contain another directory and
+// one cannot create another directory there.
+// 3. The thread dies
+// After (3) happens, the directory is not available anymore in /proc.
+bool ChrootToSafeEmptyDir() {
+ base::Thread chrooter("sandbox_chrooter");
+ if (!chrooter.Start()) return false;
+ bool is_chrooted = false;
+ chrooter.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&ChrootToThreadFdInfo, chrooter.thread_id(), &is_chrooted));
+ // Make sure our task has run before committing the return value.
+ chrooter.Stop();
+ return is_chrooted;
+}
+
+// CHECK() that an attempt to move to a new user namespace raised an expected
+// errno.
+void CheckCloneNewUserErrno(int error) {
+ // EPERM can happen if already in a chroot. EUSERS if too many nested
+ // namespaces are used. EINVAL for kernels that don't support the feature.
+ // Valgrind will ENOSYS unshare().
+ PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
+ error == ENOSYS);
+}
+
+} // namespace.
+
+namespace sandbox {
+
+Credentials::Credentials() {
+}
+
+Credentials::~Credentials() {
+}
+
+int Credentials::CountOpenFds(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ int proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
+ PCHECK(0 <= proc_self_fd);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ int count = 0;
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ ++count;
+ }
+ return count;
+}
+
+bool Credentials::HasOpenDirectory(int proc_fd) {
+ int proc_self_fd = -1;
+ if (proc_fd >= 0) {
+ proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
+ } else {
+ proc_self_fd = openat(AT_FDCWD, "/proc/self/fd", O_DIRECTORY | O_RDONLY);
+ if (proc_self_fd < 0) {
+ // If this process has been chrooted (eg into /proc/self/fdinfo) then
+ // the new root dir will not have directory listing permissions for us
+ // (hence EACCES). And if we do have this permission, then /proc won't
+ // exist anyway (hence ENOENT).
+ DPCHECK(errno == EACCES || errno == ENOENT)
+ << "Unexpected failure when trying to open /proc/self/fd: ("
+ << errno << ") " << strerror(errno);
+
+ // If not available, guess false.
+ return false;
+ }
+ }
+ PCHECK(0 <= proc_self_fd);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ struct stat s;
+ // It's OK to use proc_self_fd here, fstatat won't modify it.
+ CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0);
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+ }
+
+ // No open unmanaged directories found.
+ return false;
+}
+
+bool Credentials::DropAllCapabilities() {
+ ScopedCap cap(cap_init());
+ CHECK(cap);
+ PCHECK(0 == cap_set_proc(cap.get()));
+ // We never let this function fail.
+ return true;
+}
+
+bool Credentials::HasAnyCapability() const {
+ ScopedCap current_cap(cap_get_proc());
+ CHECK(current_cap);
+ ScopedCap empty_cap(cap_init());
+ CHECK(empty_cap);
+ return cap_compare(current_cap.get(), empty_cap.get()) != 0;
+}
+
+scoped_ptr<std::string> Credentials::GetCurrentCapString() const {
+ ScopedCap current_cap(cap_get_proc());
+ CHECK(current_cap);
+ ScopedCapText cap_text(cap_to_text(current_cap.get(), NULL));
+ CHECK(cap_text);
+ return scoped_ptr<std::string> (new std::string(cap_text.get()));
+}
+
+// static
+bool Credentials::SupportsNewUserNS() {
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+ // so always consider UserNS unsupported there.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+ // This is roughly a fork().
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWUSER | SIGCHLD, 0, 0, 0);
+
+ if (pid == -1) {
+ CheckCloneNewUserErrno(errno);
+ return false;
+ }
+
+ // The parent process could have had threads. In the child, these threads
+ // have disappeared. Make sure to not do anything in the child, as this is a
+ // fragile execution environment.
+ if (pid == 0) {
+ _exit(0);
+ }
+
+ // Always reap the child.
+ siginfo_t infop;
+ PCHECK(0 == HANDLE_EINTR(waitid(P_PID, pid, &infop, WEXITED)));
+
+ // clone(2) succeeded, we can use CLONE_NEWUSER.
+ return true;
+}
+
+bool Credentials::MoveToNewUserNS() {
+ uid_t uid;
+ gid_t gid;
+ if (!GetRESIds(&uid, &gid)) {
+ // If all the uids (or gids) are not equal to each other, the security
+ // model will most likely confuse the caller, abort.
+ DVLOG(1) << "uids or gids differ!";
+ return false;
+ }
+ int ret = unshare(CLONE_NEWUSER);
+ if (ret) {
+ const int unshare_errno = errno;
+ VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
+ << "on this kernel.";
+ CheckCloneNewUserErrno(unshare_errno);
+ return false;
+ }
+
+ // The current {r,e,s}{u,g}id is now an overflow id (c.f.
+ // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
+ DCHECK(GetRESIds(NULL, NULL));
+ const char kGidMapFile[] = "/proc/self/gid_map";
+ const char kUidMapFile[] = "/proc/self/uid_map";
+ CHECK(WriteToIdMapFile(kGidMapFile, gid));
+ CHECK(WriteToIdMapFile(kUidMapFile, uid));
+ DCHECK(GetRESIds(NULL, NULL));
+ return true;
+}
+
+bool Credentials::DropFileSystemAccess() {
+ // Chrooting to a safe empty dir will only be safe if no directory file
+ // descriptor is available to the process.
+ DCHECK(!HasOpenDirectory(-1));
+ return ChrootToSafeEmptyDir();
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
new file mode 100644
index 0000000..99f8f32
--- /dev/null
+++ b/sandbox/linux/services/credentials.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
+#define SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
+
+#include "build/build_config.h"
+// Link errors are tedious to track, raise a compile-time error instead.
+#if defined(OS_ANDROID)
+#error "Android is not supported."
+#endif // defined(OS_ANDROID).
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This class should be used to manipulate the current process' credentials.
+// It is currently a stub used to manipulate POSIX.1e capabilities as
+// implemented by the Linux kernel.
+class SANDBOX_EXPORT Credentials {
+ public:
+ Credentials();
+ ~Credentials();
+
+ // Returns the number of file descriptors in the current process's FD
+ // table, excluding |proc_fd|, which should be a file descriptor for
+ // /proc.
+ int CountOpenFds(int proc_fd);
+
+ // Checks whether the current process has any directory file descriptor open.
+ // Directory file descriptors are "capabilities" that would let a process use
+ // system calls such as openat() to bypass restrictions such as
+ // DropFileSystemAccess().
+ // Sometimes it's useful to call HasOpenDirectory() after file system access
+ // has been dropped. In this case, |proc_fd| should be a file descriptor to
+ // /proc. The file descriptor in |proc_fd| will be ignored by
+ // HasOpenDirectory() and remains owned by the caller. It is very important
+ // for the caller to close it.
+ // If /proc is available, |proc_fd| can be passed as -1.
+ // If |proc_fd| is -1 and /proc is not available, this function will return
+ // false.
+ bool HasOpenDirectory(int proc_fd);
+
+ // Drop all capabilities in the effective, inheritable and permitted sets for
+ // the current process.
+ bool DropAllCapabilities();
+ // Return true iff there is any capability in any of the capabilities sets
+ // of the current process.
+ bool HasAnyCapability() const;
+ // Returns the capabilities of the current process in textual form, as
+ // documented in libcap2's cap_to_text(3). This is mostly useful for
+ // debugging and tests.
+ scoped_ptr<std::string> GetCurrentCapString() const;
+
+ // Returns whether the kernel supports CLONE_NEWUSER and whether it would be
+ // possible to immediately move to a new user namespace. There is no point
+ // in using this method right before calling MoveToNewUserNS(), simply call
+ // MoveToNewUserNS() immediately. This method is only useful to test kernel
+ // support ahead of time.
+ static bool SupportsNewUserNS();
+
+ // Move the current process to a new "user namespace" as supported by Linux
+ // 3.8+ (CLONE_NEWUSER).
+ // The uid map will be set-up so that the perceived uid and gid will not
+ // change.
+ // If this call succeeds, the current process will be granted a full set of
+ // capabilities in the new namespace.
+ bool MoveToNewUserNS();
+
+ // Remove the ability of the process to access the file system. File
+ // descriptors which are already open prior to calling this API remain
+ // available.
+ // The implementation currently uses chroot(2) and requires CAP_SYS_CHROOT.
+ // CAP_SYS_CHROOT can be acquired by using the MoveToNewUserNS() API.
+ // Make sure to call DropAllCapabilities() after this call to prevent
+ // escapes.
+ // To be secure, it's very important for this API to not be called while the
+ // process has any directory file descriptor open.
+ bool DropFileSystemAccess();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Credentials);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
new file mode 100644
index 0000000..28199fb
--- /dev/null
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/credentials.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool DirectoryExists(const char* path) {
+ struct stat dir;
+ errno = 0;
+ int ret = stat(path, &dir);
+ return -1 != ret || ENOENT != errno;
+}
+
+bool WorkingDirectoryIsRoot() {
+ char current_dir[PATH_MAX];
+ char* cwd = getcwd(current_dir, sizeof(current_dir));
+ PCHECK(cwd);
+ if (strcmp("/", cwd)) return false;
+
+ // The current directory is the root. Add a few paranoid checks.
+ struct stat current;
+ CHECK_EQ(0, stat(".", ¤t));
+ struct stat parrent;
+ CHECK_EQ(0, stat("..", &parrent));
+ CHECK_EQ(current.st_dev, parrent.st_dev);
+ CHECK_EQ(current.st_ino, parrent.st_ino);
+ CHECK_EQ(current.st_mode, parrent.st_mode);
+ CHECK_EQ(current.st_uid, parrent.st_uid);
+ CHECK_EQ(current.st_gid, parrent.st_gid);
+ return true;
+}
+
+// Give dynamic tools a simple thing to test.
+TEST(Credentials, CreateAndDestroy) {
+ {
+ Credentials cred1;
+ (void) cred1;
+ }
+ scoped_ptr<Credentials> cred2(new Credentials);
+}
+
+TEST(Credentials, CountOpenFds) {
+ base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
+ ASSERT_TRUE(proc_fd.is_valid());
+ Credentials creds;
+ int fd_count = creds.CountOpenFds(proc_fd.get());
+ int fd = open("/dev/null", O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(fd_count + 1, creds.CountOpenFds(proc_fd.get()));
+ ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
+ EXPECT_EQ(fd_count, creds.CountOpenFds(proc_fd.get()));
+}
+
+TEST(Credentials, HasOpenDirectory) {
+ Credentials creds;
+ // No open directory should exist at startup.
+ EXPECT_FALSE(creds.HasOpenDirectory(-1));
+ {
+ // Have a "/dev" file descriptor around.
+ int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD dev_fd_closer(dev_fd);
+ EXPECT_TRUE(creds.HasOpenDirectory(-1));
+ }
+ EXPECT_FALSE(creds.HasOpenDirectory(-1));
+}
+
+TEST(Credentials, HasOpenDirectoryWithFD) {
+ Credentials creds;
+
+ int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD proc_fd_closer(proc_fd);
+ ASSERT_LE(0, proc_fd);
+
+ // Don't pass |proc_fd|, an open directory (proc_fd) should
+ // be detected.
+ EXPECT_TRUE(creds.HasOpenDirectory(-1));
+ // Pass |proc_fd| and no open directory should be detected.
+ EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
+
+ {
+ // Have a "/dev" file descriptor around.
+ int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD dev_fd_closer(dev_fd);
+ EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
+ }
+
+ // The "/dev" file descriptor should now be closed, |proc_fd| is the only
+ // directory file descriptor open.
+ EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
+}
+
+SANDBOX_TEST(Credentials, DropAllCaps) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ CHECK(!creds.HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, GetCurrentCapString) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ const char kNoCapabilityText[] = "=";
+ CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
+}
+
+SANDBOX_TEST(Credentials, MoveToNewUserNS) {
+ Credentials creds;
+ creds.DropAllCapabilities();
+ bool moved_to_new_ns = creds.MoveToNewUserNS();
+ fprintf(stdout,
+ "Unprivileged CLONE_NEWUSER supported: %s\n",
+ moved_to_new_ns ? "true." : "false.");
+ fflush(stdout);
+ if (!moved_to_new_ns) {
+ fprintf(stdout, "This kernel does not support unprivileged namespaces. "
+ "USERNS tests will succeed without running.\n");
+ fflush(stdout);
+ return;
+ }
+ CHECK(creds.HasAnyCapability());
+ creds.DropAllCapabilities();
+ CHECK(!creds.HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, SupportsUserNS) {
+ Credentials creds;
+ creds.DropAllCapabilities();
+ bool user_ns_supported = Credentials::SupportsNewUserNS();
+ bool moved_to_new_ns = creds.MoveToNewUserNS();
+ CHECK_EQ(user_ns_supported, moved_to_new_ns);
+}
+
+SANDBOX_TEST(Credentials, UidIsPreserved) {
+ Credentials creds;
+ creds.DropAllCapabilities();
+ uid_t old_ruid, old_euid, old_suid;
+ gid_t old_rgid, old_egid, old_sgid;
+ PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
+ PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ uid_t new_ruid, new_euid, new_suid;
+ PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
+ CHECK(old_ruid == new_ruid);
+ CHECK(old_euid == new_euid);
+ CHECK(old_suid == new_suid);
+
+ gid_t new_rgid, new_egid, new_sgid;
+ PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
+ CHECK(old_rgid == new_rgid);
+ CHECK(old_egid == new_egid);
+ CHECK(old_sgid == new_sgid);
+}
+
+bool NewUserNSCycle(Credentials* creds) {
+ DCHECK(creds);
+ if (!creds->MoveToNewUserNS() ||
+ !creds->HasAnyCapability() ||
+ !creds->DropAllCapabilities() ||
+ creds->HasAnyCapability()) {
+ return false;
+ }
+ return true;
+}
+
+SANDBOX_TEST(Credentials, NestedUserNS) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ creds.DropAllCapabilities();
+ // As of 3.12, the kernel has a limit of 32. See create_user_ns().
+ const int kNestLevel = 10;
+ for (int i = 0; i < kNestLevel; ++i) {
+ CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration "
+ << i << ".";
+ }
+}
+
+// Test the WorkingDirectoryIsRoot() helper.
+TEST(Credentials, CanDetectRoot) {
+ ASSERT_EQ(0, chdir("/proc/"));
+ ASSERT_FALSE(WorkingDirectoryIsRoot());
+ ASSERT_EQ(0, chdir("/"));
+ ASSERT_TRUE(WorkingDirectoryIsRoot());
+}
+
+SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(DropFileSystemAccessIsSafe)) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ CHECK(creds.DropFileSystemAccess());
+ CHECK(!DirectoryExists("/proc"));
+ CHECK(WorkingDirectoryIsRoot());
+ // We want the chroot to never have a subdirectory. A subdirectory
+ // could allow a chroot escape.
+ CHECK_NE(0, mkdir("/test", 0700));
+}
+
+// Check that after dropping filesystem access and dropping privileges
+// it is not possible to regain capabilities.
+SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(CannotRegainPrivileges)) {
+ Credentials creds;
+ CHECK(creds.DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!creds.MoveToNewUserNS()) return;
+ CHECK(creds.DropFileSystemAccess());
+ CHECK(creds.DropAllCapabilities());
+
+ // The kernel should now prevent us from regaining capabilities because we
+ // are in a chroot.
+ CHECK(!Credentials::SupportsNewUserNS());
+ CHECK(!creds.MoveToNewUserNS());
+}
+
+} // namespace.
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.cc b/sandbox/linux/services/init_process_reaper.cc
new file mode 100644
index 0000000..2e0b90b
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.cc
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/init_process_reaper.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoNothingSignalHandler(int signal) {}
+
+} // namespace
+
+bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) {
+ int sync_fds[2];
+ // We want to use send, so we can't use a pipe
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+ PLOG(ERROR) << "Failed to create socketpair";
+ return false;
+ }
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ return false;
+ }
+ if (child_pid) {
+ // In the parent, assuming the role of an init process.
+ // The disposition for SIGCHLD cannot be SIG_IGN or wait() will only return
+ // once all of our childs are dead. Since we're init we need to reap childs
+ // as they come.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = &DoNothingSignalHandler;
+ CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[1], SHUT_RD);
+ DPCHECK(!close_ret);
+ if (post_fork_parent_callback)
+ post_fork_parent_callback->Run();
+ // Tell the child to continue
+ CHECK(HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) == 1);
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+
+ for (;;) {
+ // Loop until we have reaped our one natural child
+ siginfo_t reaped_child_info;
+ int wait_ret =
+ HANDLE_EINTR(waitid(P_ALL, 0, &reaped_child_info, WEXITED));
+ if (wait_ret)
+ _exit(1);
+ if (reaped_child_info.si_pid == child_pid) {
+ int exit_code = 0;
+ // We're done waiting
+ if (reaped_child_info.si_code == CLD_EXITED) {
+ exit_code = reaped_child_info.si_status;
+ }
+ // Exit with the same exit code as our parent. Exit with 0 if we got
+ // signaled.
+ _exit(exit_code);
+ }
+ }
+ } else {
+ // The child needs to wait for the parent to run the callback to avoid a
+ // race condition.
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[0], SHUT_WR);
+ DPCHECK(!close_ret);
+ char should_continue;
+ int read_ret = HANDLE_EINTR(read(sync_fds[0], &should_continue, 1));
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ if (read_ret == 1)
+ return true;
+ else
+ return false;
+ }
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.h b/sandbox/linux/services/init_process_reaper.h
new file mode 100644
index 0000000..840f6fc
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.h
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+#define SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+
+#include "base/callback_forward.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// The current process will fork(). The parent will become a process reaper
+// like init(1). The child will continue normally (after this function
+// returns).
+// If not NULL, |post_fork_parent_callback| will run in the parent almost
+// immediately after fork().
+// Since this function calls fork(), it's very important that the caller has
+// only one thread running.
+SANDBOX_EXPORT bool CreateInitProcessReaper(
+ base::Closure* post_fork_parent_callback);
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
diff --git a/sandbox/linux/services/libc_urandom_override.cc b/sandbox/linux/services/libc_urandom_override.cc
new file mode 100644
index 0000000..33bb25d
--- /dev/null
+++ b/sandbox/linux/services/libc_urandom_override.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/libc_urandom_override.h"
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/rand_util.h"
+
+// Note: this file is used by the zygote and nacl_helper.
+
+#if !defined(HAVE_XSTAT) && defined(LIBC_GLIBC)
+#define HAVE_XSTAT 1
+#endif
+
+namespace sandbox {
+
+static bool g_override_urandom = false;
+
+// TODO(sergeyu): Currently InitLibcUrandomOverrides() doesn't work properly
+// under ASan or MSan - it crashes content_unittests. Make sure it works
+// properly and enable it here. http://crbug.com/123263
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+static void InitLibcFileIOFunctions();
+static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
+#endif
+
+void InitLibcUrandomOverrides() {
+ // Make sure /dev/urandom is open.
+ base::GetUrandomFD();
+ g_override_urandom = true;
+
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+#endif
+}
+
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+
+static const char kUrandomDevPath[] = "/dev/urandom";
+
+typedef FILE* (*FopenFunction)(const char* path, const char* mode);
+
+static FopenFunction g_libc_fopen = NULL;
+static FopenFunction g_libc_fopen64 = NULL;
+
+#if HAVE_XSTAT
+typedef int (*XstatFunction)(int version, const char *path, struct stat *buf);
+typedef int (*Xstat64Function)(int version, const char *path,
+ struct stat64 *buf);
+
+static XstatFunction g_libc_xstat = NULL;
+static Xstat64Function g_libc_xstat64 = NULL;
+#else
+typedef int (*StatFunction)(const char *path, struct stat *buf);
+typedef int (*Stat64Function)(const char *path, struct stat64 *buf);
+
+static StatFunction g_libc_stat = NULL;
+static Stat64Function g_libc_stat64 = NULL;
+#endif // HAVE_XSTAT
+
+// Find the libc's real fopen* and *stat* functions. This should only be
+// called once, and should be guarded by g_libc_file_io_funcs_guard.
+static void InitLibcFileIOFunctions() {
+ g_libc_fopen = reinterpret_cast<FopenFunction>(
+ dlsym(RTLD_NEXT, "fopen"));
+ g_libc_fopen64 = reinterpret_cast<FopenFunction>(
+ dlsym(RTLD_NEXT, "fopen64"));
+
+ if (!g_libc_fopen) {
+ LOG(FATAL) << "Failed to get fopen() from libc.";
+ } else if (!g_libc_fopen64) {
+#if !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
+ LOG(WARNING) << "Failed to get fopen64() from libc. Using fopen() instead.";
+#endif // !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
+ g_libc_fopen64 = g_libc_fopen;
+ }
+
+#if HAVE_XSTAT
+ g_libc_xstat = reinterpret_cast<XstatFunction>(
+ dlsym(RTLD_NEXT, "__xstat"));
+ g_libc_xstat64 = reinterpret_cast<Xstat64Function>(
+ dlsym(RTLD_NEXT, "__xstat64"));
+
+ if (!g_libc_xstat) {
+ LOG(FATAL) << "Failed to get __xstat() from libc.";
+ }
+ if (!g_libc_xstat64) {
+ LOG(FATAL) << "Failed to get __xstat64() from libc.";
+ }
+#else
+ g_libc_stat = reinterpret_cast<StatFunction>(
+ dlsym(RTLD_NEXT, "stat"));
+ g_libc_stat64 = reinterpret_cast<Stat64Function>(
+ dlsym(RTLD_NEXT, "stat64"));
+
+ if (!g_libc_stat) {
+ LOG(FATAL) << "Failed to get stat() from libc.";
+ }
+ if (!g_libc_stat64) {
+ LOG(FATAL) << "Failed to get stat64() from libc.";
+ }
+#endif // HAVE_XSTAT
+}
+
+// fopen() and fopen64() are intercepted here so that NSS can open
+// /dev/urandom to seed its random number generator. NSS is used by
+// remoting in the sendbox.
+
+// fopen() call may be redirected to fopen64() in stdio.h using
+// __REDIRECT(), which sets asm name for fopen() to "fopen64". This
+// means that we cannot override fopen() directly here. Instead the
+// the code below defines fopen_override() function with asm name
+// "fopen", so that all references to fopen() will resolve to this
+// function.
+__attribute__ ((__visibility__("default")))
+FILE* fopen_override(const char* path, const char* mode) __asm__ ("fopen");
+
+__attribute__ ((__visibility__("default")))
+FILE* fopen_override(const char* path, const char* mode) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
+ if (fd < 0) {
+ PLOG(ERROR) << "dup() failed.";
+ return NULL;
+ }
+ return fdopen(fd, mode);
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_fopen(path, mode);
+ }
+}
+
+__attribute__ ((__visibility__("default")))
+FILE* fopen64(const char* path, const char* mode) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
+ if (fd < 0) {
+ PLOG(ERROR) << "dup() failed.";
+ return NULL;
+ }
+ return fdopen(fd, mode);
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_fopen64(path, mode);
+ }
+}
+
+// The stat() family of functions are subject to the same problem as
+// fopen(), so we have to use the same trick to override them.
+
+#if HAVE_XSTAT
+
+__attribute__ ((__visibility__("default")))
+int xstat_override(int version,
+ const char *path,
+ struct stat *buf) __asm__ ("__xstat");
+
+__attribute__ ((__visibility__("default")))
+int xstat_override(int version, const char *path, struct stat *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = __fxstat(version, base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_xstat(version, path, buf);
+ }
+}
+
+__attribute__ ((__visibility__("default")))
+int xstat64_override(int version,
+ const char *path,
+ struct stat64 *buf) __asm__ ("__xstat64");
+
+__attribute__ ((__visibility__("default")))
+int xstat64_override(int version, const char *path, struct stat64 *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = __fxstat64(version, base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_xstat64(version, path, buf);
+ }
+}
+
+#else
+
+__attribute__ ((__visibility__("default")))
+int stat_override(const char *path,
+ struct stat *buf) __asm__ ("stat");
+
+__attribute__ ((__visibility__("default")))
+int stat_override(const char *path, struct stat *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = fstat(base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_stat(path, buf);
+ }
+}
+
+__attribute__ ((__visibility__("default")))
+int stat64_override(const char *path,
+ struct stat64 *buf) __asm__ ("stat64");
+
+__attribute__ ((__visibility__("default")))
+int stat64_override(const char *path, struct stat64 *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = fstat64(base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_stat64(path, buf);
+ }
+}
+
+#endif // HAVE_XSTAT
+
+#endif // !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+
+} // namespace content
diff --git a/sandbox/linux/services/libc_urandom_override.h b/sandbox/linux/services/libc_urandom_override.h
new file mode 100644
index 0000000..86212f8
--- /dev/null
+++ b/sandbox/linux/services/libc_urandom_override.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_LIBC_URANDOM_OVERRIDE_H_
+#define SANDBOX_LINUX_SERVICES_LIBC_URANDOM_OVERRIDE_H_
+
+namespace sandbox {
+
+void InitLibcUrandomOverrides();
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_LIBC_URANDOM_OVERRIDE_H_
diff --git a/sandbox/linux/services/linux_syscalls.h b/sandbox/linux/services/linux_syscalls.h
new file mode 100644
index 0000000..b6a40c6
--- /dev/null
+++ b/sandbox/linux/services/linux_syscalls.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This header will be kept up to date so that we can compile system-call
+// policies even when system headers are old.
+// System call numbers are accessible through __NR_syscall_name.
+
+#ifndef SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_
+
+#if defined(__x86_64__)
+#include "sandbox/linux/services/x86_64_linux_syscalls.h"
+#endif
+
+#if defined(__i386__)
+#include "sandbox/linux/services/x86_32_linux_syscalls.h"
+#endif
+
+#if defined(__arm__) && defined(__ARM_EABI__)
+#include "sandbox/linux/services/arm_linux_syscalls.h"
+#endif
+
+#if defined(__mips__) && defined(_ABIO32)
+#include "sandbox/linux/services/mips_linux_syscalls.h"
+#endif
+
+#if defined(__aarch64__)
+#include "sandbox/linux/services/arm64_linux_syscalls.h"
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/services/mips_linux_syscalls.h b/sandbox/linux/services/mips_linux_syscalls.h
new file mode 100644
index 0000000..14daff9
--- /dev/null
+++ b/sandbox/linux/services/mips_linux_syscalls.h
@@ -0,0 +1,1428 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's calls.S.
+#ifndef SANDBOX_LINUX_SERVICES_MIPS_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_MIPS_LINUX_SYSCALLS_H_
+
+#if !defined(__mips__) || !defined(_ABIO32)
+#error "Including header on wrong architecture"
+#endif
+
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+
+#if !defined(__NR_syscall)
+#define __NR_syscall (__NR_Linux + 0)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_Linux + 1)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_Linux + 2)
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_Linux + 3)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_Linux + 4)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_Linux + 5)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_Linux + 6)
+#endif
+
+#if !defined(__NR_waitpid)
+#define __NR_waitpid (__NR_Linux + 7)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_Linux + 8)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_Linux + 9)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_Linux + 10)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_Linux + 11)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_Linux + 12)
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time (__NR_Linux + 13)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_Linux + 14)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_Linux + 15)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_Linux + 16)
+#endif
+
+#if !defined(__NR_break)
+#define __NR_break (__NR_Linux + 17)
+#endif
+
+#if !defined(__NR_unused18)
+#define __NR_unused18 (__NR_Linux + 18)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_Linux + 19)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_Linux + 20)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_Linux + 21)
+#endif
+
+#if !defined(__NR_umount)
+#define __NR_umount (__NR_Linux + 22)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_Linux + 23)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_Linux + 24)
+#endif
+
+#if !defined(__NR_stime)
+#define __NR_stime (__NR_Linux + 25)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_Linux + 26)
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm (__NR_Linux + 27)
+#endif
+
+#if !defined(__NR_unused28)
+#define __NR_unused28 (__NR_Linux + 28)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_Linux + 29)
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime (__NR_Linux + 30)
+#endif
+
+#if !defined(__NR_stty)
+#define __NR_stty (__NR_Linux + 31)
+#endif
+
+#if !defined(__NR_gtty)
+#define __NR_gtty (__NR_Linux + 32)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_Linux + 33)
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice (__NR_Linux + 34)
+#endif
+
+#if !defined(__NR_ftime)
+#define __NR_ftime (__NR_Linux + 35)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_Linux + 36)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_Linux + 37)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_Linux + 38)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_Linux + 39)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_Linux + 40)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_Linux + 41)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_Linux + 42)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_Linux + 43)
+#endif
+
+#if !defined(__NR_prof)
+#define __NR_prof (__NR_Linux + 44)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_Linux + 45)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_Linux + 46)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_Linux + 47)
+#endif
+
+#if !defined(__NR_signal)
+#define __NR_signal (__NR_Linux + 48)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_Linux + 49)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_Linux + 50)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_Linux + 51)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_Linux + 52)
+#endif
+
+#if !defined(__NR_lock)
+#define __NR_lock (__NR_Linux + 53)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_Linux + 54)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_Linux + 55)
+#endif
+
+#if !defined(__NR_mpx)
+#define __NR_mpx (__NR_Linux + 56)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_Linux + 57)
+#endif
+
+#if !defined(__NR_ulimit)
+#define __NR_ulimit (__NR_Linux + 58)
+#endif
+
+#if !defined(__NR_unused59)
+#define __NR_unused59 (__NR_Linux + 59)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_Linux + 60)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_Linux + 61)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_Linux + 62)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_Linux + 63)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_Linux + 64)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_Linux + 65)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_Linux + 66)
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction (__NR_Linux + 67)
+#endif
+
+#if !defined(__NR_sgetmask)
+#define __NR_sgetmask (__NR_Linux + 68)
+#endif
+
+#if !defined(__NR_ssetmask)
+#define __NR_ssetmask (__NR_Linux + 69)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_Linux + 70)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_Linux + 71)
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend (__NR_Linux + 72)
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending (__NR_Linux + 73)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_Linux + 74)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_Linux + 75)
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit (__NR_Linux + 76)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_Linux + 77)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_Linux + 78)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_Linux + 79)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_Linux + 80)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_Linux + 81)
+#endif
+
+#if !defined(__NR_reserved82)
+#define __NR_reserved82 (__NR_Linux + 82)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_Linux + 83)
+#endif
+
+#if !defined(__NR_unused84)
+#define __NR_unused84 (__NR_Linux + 84)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_Linux + 85)
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib (__NR_Linux + 86)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_Linux + 87)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_Linux + 88)
+#endif
+
+#if !defined(__NR_readdir)
+#define __NR_readdir (__NR_Linux + 89)
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap (__NR_Linux + 90)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_Linux + 91)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_Linux + 92)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_Linux + 93)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_Linux + 94)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_Linux + 95)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_Linux + 96)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_Linux + 97)
+#endif
+
+#if !defined(__NR_profil)
+#define __NR_profil (__NR_Linux + 98)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_Linux + 99)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_Linux + 100)
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm (__NR_Linux + 101)
+#endif
+
+#if !defined(__NR_socketcall)
+#define __NR_socketcall (__NR_Linux + 102)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_Linux + 103)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_Linux + 104)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_Linux + 105)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_Linux + 106)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_Linux + 107)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_Linux + 108)
+#endif
+
+#if !defined(__NR_unused109)
+#define __NR_unused109 (__NR_Linux + 109)
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl (__NR_Linux + 110)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_Linux + 111)
+#endif
+
+#if !defined(__NR_idle)
+#define __NR_idle (__NR_Linux + 112)
+#endif
+
+#if !defined(__NR_vm86)
+#define __NR_vm86 (__NR_Linux + 113)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_Linux + 114)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_Linux + 115)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_Linux + 116)
+#endif
+
+#if !defined(__NR_ipc)
+#define __NR_ipc (__NR_Linux + 117)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_Linux + 118)
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn (__NR_Linux + 119)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_Linux + 120)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_Linux + 121)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_Linux + 122)
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt (__NR_Linux + 123)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_Linux + 124)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_Linux + 125)
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask (__NR_Linux + 126)
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module (__NR_Linux + 127)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_Linux + 128)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_Linux + 129)
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms (__NR_Linux + 130)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_Linux + 131)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_Linux + 132)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_Linux + 133)
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush (__NR_Linux + 134)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_Linux + 135)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_Linux + 136)
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall \
+ (__NR_Linux + 137) /* Syscall for Andrew File System \
+ */
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_Linux + 138)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_Linux + 139)
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek (__NR_Linux + 140)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_Linux + 141)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_Linux + 142)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_Linux + 143)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_Linux + 144)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_Linux + 145)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_Linux + 146)
+#endif
+
+#if !defined(__NR_cacheflush)
+#define __NR_cacheflush (__NR_Linux + 147)
+#endif
+
+#if !defined(__NR_cachectl)
+#define __NR_cachectl (__NR_Linux + 148)
+#endif
+
+#if !defined(__NR_sysmips)
+#define __NR_sysmips (__NR_Linux + 149)
+#endif
+
+#if !defined(__NR_unused150)
+#define __NR_unused150 (__NR_Linux + 150)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_Linux + 151)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_Linux + 152)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_Linux + 153)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_Linux + 154)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_Linux + 155)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_Linux + 156)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_Linux + 157)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_Linux + 158)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_Linux + 159)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_Linux + 160)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_Linux + 161)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_Linux + 162)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_Linux + 163)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_Linux + 164)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_Linux + 165)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_Linux + 166)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_Linux + 167)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_Linux + 168)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_Linux + 169)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_Linux + 170)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_Linux + 171)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_Linux + 172)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_Linux + 173)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_Linux + 174)
+#endif
+
+#if !defined(__NR_recv)
+#define __NR_recv (__NR_Linux + 175)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_Linux + 176)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_Linux + 177)
+#endif
+
+#if !defined(__NR_send)
+#define __NR_send (__NR_Linux + 178)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_Linux + 179)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_Linux + 180)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_Linux + 181)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_Linux + 182)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_Linux + 183)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_Linux + 184)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_Linux + 185)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_Linux + 186)
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module (__NR_Linux + 187)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_Linux + 188)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_Linux + 189)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_Linux + 190)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_Linux + 191)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_Linux + 192)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_Linux + 193)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_Linux + 194)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_Linux + 195)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_Linux + 196)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_Linux + 197)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_Linux + 198)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_Linux + 199)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_Linux + 200)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_Linux + 201)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_Linux + 202)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_Linux + 203)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_Linux + 204)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_Linux + 205)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_Linux + 206)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_Linux + 207)
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg (__NR_Linux + 208)
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg (__NR_Linux + 209)
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 (__NR_Linux + 210)
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 (__NR_Linux + 211)
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 (__NR_Linux + 212)
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 (__NR_Linux + 213)
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 (__NR_Linux + 214)
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 (__NR_Linux + 215)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_Linux + 216)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_Linux + 217)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_Linux + 218)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_Linux + 219)
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 (__NR_Linux + 220)
+#endif
+
+#if !defined(__NR_reserved221)
+#define __NR_reserved221 (__NR_Linux + 221)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_Linux + 222)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_Linux + 223)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_Linux + 224)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_Linux + 225)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_Linux + 226)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_Linux + 227)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_Linux + 228)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_Linux + 229)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_Linux + 230)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_Linux + 231)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_Linux + 232)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_Linux + 233)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_Linux + 234)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_Linux + 235)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_Linux + 236)
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 (__NR_Linux + 237)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_Linux + 238)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_Linux + 239)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_Linux + 240)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_Linux + 241)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_Linux + 242)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_Linux + 243)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_Linux + 244)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_Linux + 245)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_Linux + 246)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_Linux + 247)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_Linux + 248)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_Linux + 249)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_Linux + 250)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_Linux + 251)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_Linux + 252)
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_Linux + 253)
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 (__NR_Linux + 254)
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 (__NR_Linux + 255)
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 (__NR_Linux + 256)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_Linux + 257)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_Linux + 258)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_Linux + 259)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_Linux + 260)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_Linux + 261)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_Linux + 262)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_Linux + 263)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_Linux + 264)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_Linux + 265)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_Linux + 266)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_Linux + 267)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_Linux + 268)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_Linux + 269)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_Linux + 270)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_Linux + 271)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_Linux + 272)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_Linux + 273)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_Linux + 274)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_Linux + 275)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_Linux + 276)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_Linux + 277)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_Linux + 278)
+#endif
+
+/* #define __NR_sys_setaltroot (__NR_Linux + 279) */
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_Linux + 280)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_Linux + 281)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_Linux + 282)
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area (__NR_Linux + 283)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_Linux + 284)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_Linux + 285)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_Linux + 286)
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages (__NR_Linux + 287)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_Linux + 288)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_Linux + 289)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_Linux + 290)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_Linux + 291)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_Linux + 292)
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 (__NR_Linux + 293)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_Linux + 294)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_Linux + 295)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_Linux + 296)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_Linux + 297)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_Linux + 298)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_Linux + 299)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_Linux + 300)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_Linux + 301)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_Linux + 302)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_Linux + 303)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_Linux + 304)
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range (__NR_Linux + 305)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_Linux + 306)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_Linux + 307)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_Linux + 308)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_Linux + 309)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_Linux + 310)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_Linux + 311)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_Linux + 312)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_Linux + 313)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_Linux + 314)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_Linux + 315)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_Linux + 316)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_Linux + 317)
+#endif
+
+#if !defined(__NR_timerfd)
+#define __NR_timerfd (__NR_Linux + 318)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_Linux + 319)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_Linux + 320)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_Linux + 321)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_Linux + 322)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_Linux + 323)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_Linux + 324)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_Linux + 325)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_Linux + 326)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_Linux + 327)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_Linux + 328)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_Linux + 329)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_Linux + 330)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_Linux + 331)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_Linux + 332)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_Linux + 333)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_Linux + 334)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_Linux + 335)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_Linux + 336)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_Linux + 337)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_Linux + 338)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_Linux + 339)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_Linux + 340)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_Linux + 341)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_Linux + 342)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_Linux + 343)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_Linux + 344)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_Linux + 345)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_Linux + 346)
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_Linux + 347)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_Linux + 348)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_Linux + 349)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_Linux + 350)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_Linux + 351)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_Linux + 352)
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_MIPS_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/services/scoped_process.cc b/sandbox/linux/services/scoped_process.cc
new file mode 100644
index 0000000..fd42a2a
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/scoped_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/thread_helpers.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSynchronisationChar[] = "D";
+
+void WaitForever() {
+ while(true) {
+ pause();
+ }
+}
+
+} // namespace
+
+ScopedProcess::ScopedProcess(const base::Closure& child_callback)
+ : child_process_id_(-1), process_id_(getpid()) {
+ PCHECK(0 == pipe(pipe_fds_));
+#if !defined(THREAD_SANITIZER)
+ // Make sure that we can safely fork().
+ CHECK(ThreadHelpers::IsSingleThreaded(-1));
+#endif
+ child_process_id_ = fork();
+ PCHECK(0 <= child_process_id_);
+
+ if (0 == child_process_id_) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ pipe_fds_[0] = -1;
+ child_callback.Run();
+ // Notify the parent that the closure has run.
+ CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1)));
+ WaitForever();
+ NOTREACHED();
+ _exit(1);
+ }
+
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ pipe_fds_[1] = -1;
+}
+
+ScopedProcess::~ScopedProcess() {
+ CHECK(IsOriginalProcess());
+ if (child_process_id_ >= 0) {
+ PCHECK(0 == kill(child_process_id_, SIGKILL));
+ siginfo_t process_info;
+
+ PCHECK(0 == HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED)));
+ }
+ if (pipe_fds_[0] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ }
+ if (pipe_fds_[1] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ }
+}
+
+int ScopedProcess::WaitForExit(bool* got_signaled) {
+ DCHECK(got_signaled);
+ CHECK(IsOriginalProcess());
+ siginfo_t process_info;
+ // WNOWAIT to make sure that the destructor can wait on the child.
+ int ret = HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT));
+ PCHECK(0 == ret) << "Did something else wait on the child?";
+
+ if (process_info.si_code == CLD_EXITED) {
+ *got_signaled = false;
+ } else if (process_info.si_code == CLD_KILLED ||
+ process_info.si_code == CLD_DUMPED) {
+ *got_signaled = true;
+ } else {
+ CHECK(false) << "ScopedProcess needs to be extended for si_code "
+ << process_info.si_code;
+ }
+ return process_info.si_status;
+}
+
+bool ScopedProcess::WaitForClosureToRun() {
+ char c = 0;
+ int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1));
+ PCHECK(ret >= 0);
+ if (0 == ret)
+ return false;
+
+ CHECK_EQ(c, kSynchronisationChar[0]);
+ return true;
+}
+
+// It would be problematic if after a fork(), another process would start using
+// this object.
+// This method allows to assert it is not happening.
+bool ScopedProcess::IsOriginalProcess() {
+ // Make a direct syscall to bypass glibc caching of PIDs.
+ int pid = syscall(__NR_getpid);
+ return pid == process_id_;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/scoped_process.h b/sandbox/linux/services/scoped_process.h
new file mode 100644
index 0000000..a28131e
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/process/process_handle.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// fork() a child process that will run a Closure.
+// After the Closure has run, the child will pause forever. If this object
+// is detroyed, the child will be destroyed, even if the closure did not
+// finish running. It's ok to signal the child from outside of this class to
+// destroy it.
+// This class cannot be instanciated from a multi-threaded process, as it needs
+// to fork().
+class SANDBOX_EXPORT ScopedProcess {
+ public:
+ // A new process will be created and |child_callback| will run in the child
+ // process. This callback is allowed to terminate the process or to simply
+ // return. If the callback returns, the process will wait forever.
+ explicit ScopedProcess(const base::Closure& child_callback);
+ ~ScopedProcess();
+
+ // Wait for the process to exit.
+ // |got_signaled| tells how to interpret the return value: either as an exit
+ // code, or as a signal number.
+ // When this returns, the process will still not have been reaped and will
+ // survive as a zombie for the lifetime of this object. This method can be
+ // called multiple times.
+ int WaitForExit(bool* got_signaled);
+
+ // Wait for the |child_callback| passed at construction to run. Return false
+ // if |child_callback| did not finish running and we know it never will (for
+ // instance the child crashed or used _exit()).
+ bool WaitForClosureToRun();
+ base::ProcessId GetPid() { return child_process_id_; }
+
+ private:
+ bool IsOriginalProcess();
+
+ base::ProcessId child_process_id_;
+ base::ProcessId process_id_;
+ int pipe_fds_[2];
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcess);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
diff --git a/sandbox/linux/services/scoped_process_unittest.cc b/sandbox/linux/services/scoped_process_unittest.cc
new file mode 100644
index 0000000..82b85b9
--- /dev/null
+++ b/sandbox/linux/services/scoped_process_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoExit() { _exit(0); }
+
+void ExitWithCode(int exit_code) { _exit(exit_code); }
+
+void RaiseAndExit(int signal) {
+ PCHECK(0 == raise(signal));
+ _exit(0);
+}
+
+void DoNothing() {}
+
+TEST(ScopedProcess, ScopedProcessNormalExit) {
+ const int kCustomExitCode = 12;
+ ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
+ bool got_signaled = true;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_FALSE(got_signaled);
+ EXPECT_EQ(kCustomExitCode, exit_code);
+
+ // Verify that WaitForExit() can be called multiple times on the same
+ // process.
+ bool got_signaled2 = true;
+ int exit_code2 = process.WaitForExit(&got_signaled2);
+ EXPECT_FALSE(got_signaled2);
+ EXPECT_EQ(kCustomExitCode, exit_code2);
+}
+
+// Disable this test on Android, SIGABRT is funky there.
+TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) {
+ PCHECK(SIG_ERR != signal(SIGABRT, SIG_DFL));
+ ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT));
+ bool got_signaled = false;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGABRT, exit_code);
+}
+
+TEST(ScopedProcess, ScopedProcessSignaled) {
+ ScopedProcess process(base::Bind(&DoNothing));
+ bool got_signaled = false;
+ ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGKILL, exit_code);
+}
+
+TEST(ScopedProcess, DiesForReal) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ { ScopedProcess process(base::Bind(&DoExit)); }
+
+ // Close writing end of the pipe.
+ write_end_closer.reset();
+ pipe_fds[1] = -1;
+
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c;
+ // If the child process is dead for real, there will be no writing end
+ // for this pipe left and read will EOF instead of returning EWOULDBLOCK.
+ ASSERT_EQ(0, read(pipe_fds[0], &c, 1));
+}
+
+TEST(ScopedProcess, SynchronizationBasic) {
+ ScopedProcess process1(base::Bind(&DoNothing));
+ EXPECT_TRUE(process1.WaitForClosureToRun());
+
+ ScopedProcess process2(base::Bind(&DoExit));
+ // The closure didn't finish running normally. This case is simple enough
+ // that process.WaitForClosureToRun() should return false, even though the
+ // API does not guarantees that it will return at all.
+ EXPECT_FALSE(process2.WaitForClosureToRun());
+}
+
+void SleepInMsAndWriteOneByte(int time_to_sleep, int fd) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(time_to_sleep));
+ CHECK(1 == write(fd, "1", 1));
+}
+
+TEST(ScopedProcess, SynchronizationWorks) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ // Start a process with a closure that takes a little bit to run.
+ ScopedProcess process(
+ base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1]));
+ EXPECT_TRUE(process.WaitForClosureToRun());
+
+ // Verify that the closure did, indeed, run.
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c = 0;
+ EXPECT_EQ(1, read(pipe_fds[0], &c, 1));
+ EXPECT_EQ('1', c);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.cc b/sandbox/linux/services/thread_helpers.cc
new file mode 100644
index 0000000..e820449
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/thread_helpers.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+
+namespace sandbox {
+
+namespace {
+
+bool IsSingleThreadedImpl(int proc_self_task) {
+ CHECK_LE(0, proc_self_task);
+ struct stat task_stat;
+ int fstat_ret = fstat(proc_self_task, &task_stat);
+ PCHECK(0 == fstat_ret);
+
+ // At least "..", "." and the current thread should be present.
+ CHECK_LE(3UL, task_stat.st_nlink);
+ // Counting threads via /proc/self/task could be racy. For the purpose of
+ // determining if the current proces is monothreaded it works: if at any
+ // time it becomes monothreaded, it'll stay so.
+ return task_stat.st_nlink == 3;
+}
+
+} // namespace
+
+bool ThreadHelpers::IsSingleThreaded(int proc_self_task) {
+ DCHECK_LE(-1, proc_self_task);
+ if (-1 == proc_self_task) {
+ const int task_fd = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
+ PCHECK(0 <= task_fd);
+ const bool result = IsSingleThreadedImpl(task_fd);
+ PCHECK(0 == IGNORE_EINTR(close(task_fd)));
+ return result;
+ } else {
+ return IsSingleThreadedImpl(proc_self_task);
+ }
+}
+
+bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_self_task,
+ base::Thread* thread) {
+ DCHECK_LE(0, proc_self_task);
+ DCHECK(thread);
+ const base::PlatformThreadId thread_id = thread->thread_id();
+ const std::string thread_id_dir_str = base::IntToString(thread_id) + "/";
+
+ // The kernel is at liberty to wake the thread id futex before updating
+ // /proc. Following Stop(), the thread is joined, but entries in /proc may
+ // not have been updated.
+ thread->Stop();
+
+ unsigned int iterations = 0;
+ bool thread_present_in_procfs = true;
+ // Poll /proc with an exponential back-off, sleeping 2^iterations nanoseconds
+ // in nanosleep(2).
+ // Note: the clock may not allow for nanosecond granularity, in this case the
+ // first iterations would sleep a tiny bit more instead, which would not
+ // change the calculations significantly.
+ while (thread_present_in_procfs) {
+ struct stat task_stat;
+ const int fstat_ret =
+ fstatat(proc_self_task, thread_id_dir_str.c_str(), &task_stat, 0);
+ if (fstat_ret < 0) {
+ PCHECK(ENOENT == errno);
+ // The thread disappeared from /proc, we're done.
+ thread_present_in_procfs = false;
+ break;
+ }
+ // Increase the waiting time exponentially.
+ struct timespec ts = {0, 1L << iterations /* nanoseconds */};
+ PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts)));
+ ++iterations;
+
+ // Crash after 30 iterations, which means having spent roughly 2s in
+ // nanosleep(2) cumulatively.
+ CHECK_GT(30U, iterations);
+ // In practice, this never goes through more than a couple iterations. In
+ // debug mode, crash after 64ms (+ eventually 25 times the granularity of
+ // the clock) in nanosleep(2).
+ DCHECK_GT(25U, iterations);
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.h b/sandbox/linux/services/thread_helpers.h
new file mode 100644
index 0000000..377742a
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
+#define SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
+
+#include "base/basictypes.h"
+#include "sandbox/sandbox_export.h"
+
+namespace base { class Thread; }
+
+namespace sandbox {
+
+class SANDBOX_EXPORT ThreadHelpers {
+ public:
+ // Check whether the current process is single threaded. |proc_self_tasks|
+ // can be a file descriptor to /proc/self/task/ and remains owned by the
+ // caller or -1.
+ // If |proc_self_tasks| is -1, this method will open /proc/self/task/ and
+ // crash if it cannot.
+ static bool IsSingleThreaded(int proc_self_task);
+
+ // Stop |thread| and ensure that it does not have an entry in
+ // /proc/self/task/ from the point of view of the current thread. This is
+ // the way to stop threads before calling IsSingleThreaded().
+ static bool StopThreadAndWatchProcFS(int proc_self_tasks,
+ base::Thread* thread);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadHelpers);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
diff --git a/sandbox/linux/services/thread_helpers_unittests.cc b/sandbox/linux/services/thread_helpers_unittests.cc
new file mode 100644
index 0000000..a36fd29
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers_unittests.cc
@@ -0,0 +1,111 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/thread_helpers.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::PlatformThread;
+
+namespace sandbox {
+
+namespace {
+
+int GetRaceTestIterations() {
+ if (IsRunningOnValgrind()) {
+ return 2;
+ } else {
+ return 1000;
+ }
+}
+
+class ScopedProcSelfTask {
+ public:
+ ScopedProcSelfTask() : fd_(-1) {
+ fd_ = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
+ CHECK_LE(0, fd_);
+ }
+
+ ~ScopedProcSelfTask() { PCHECK(0 == IGNORE_EINTR(close(fd_))); }
+
+ int fd() { return fd_; }
+
+ private:
+ int fd_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcSelfTask);
+};
+
+#if defined(THREAD_SANITIZER)
+// These tests fail under ThreadSanitizer, see http://crbug.com/342305
+#define MAYBE_IsSingleThreadedBasic DISABLED_IsSingleThreadedBasic
+#define MAYBE_IsSingleThreadedIterated DISABLED_IsSingleThreadedIterated
+#define MAYBE_IsSingleThreadedStartAndStop DISABLED_IsSingleThreadedStartAndStop
+#else
+#define MAYBE_IsSingleThreadedBasic IsSingleThreadedBasic
+#define MAYBE_IsSingleThreadedIterated IsSingleThreadedIterated
+#define MAYBE_IsSingleThreadedStartAndStop IsSingleThreadedStartAndStop
+#endif
+
+TEST(ThreadHelpers, MAYBE_IsSingleThreadedBasic) {
+ ScopedProcSelfTask task;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(-1));
+
+ base::Thread thread("sandbox_tests");
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(-1));
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread));
+}
+
+TEST(ThreadHelpers, MAYBE_IsSingleThreadedIterated) {
+ ScopedProcSelfTask task;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
+
+ // Iterate to check for race conditions.
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ base::Thread thread("sandbox_tests");
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread));
+ }
+}
+
+TEST(ThreadHelpers, MAYBE_IsSingleThreadedStartAndStop) {
+ ScopedProcSelfTask task;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
+
+ base::Thread thread("sandbox_tests");
+ // This is testing for a race condition, so iterate.
+ // Manually, this has been tested with more that 1M iterations.
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd()));
+
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
+ ASSERT_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+ }
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/unix_domain_socket_unittest.cc b/sandbox/linux/services/unix_domain_socket_unittest.cc
new file mode 100644
index 0000000..17208a8
--- /dev/null
+++ b/sandbox/linux/services/unix_domain_socket_unittest.cc
@@ -0,0 +1,267 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/process_handle.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+// Additional tests for base's UnixDomainSocket to make sure it behaves
+// correctly in the presence of sandboxing functionality (e.g., receiving
+// PIDs across namespaces).
+
+namespace sandbox {
+
+namespace {
+
+const char kHello[] = "hello";
+
+// If the calling process isn't root, then try using unshare(CLONE_NEWUSER)
+// to fake it.
+void FakeRoot() {
+ // If we're already root, then allow test to proceed.
+ if (geteuid() == 0)
+ return;
+
+ // Otherwise hope the kernel supports unprivileged namespaces.
+ if (unshare(CLONE_NEWUSER) == 0)
+ return;
+
+ printf("Permission to use CLONE_NEWPID missing; skipping test.\n");
+ UnitTests::IgnoreThisTest();
+}
+
+void WaitForExit(pid_t pid) {
+ int status;
+ CHECK_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(0, WEXITSTATUS(status));
+}
+
+base::ProcessId GetParentProcessId(base::ProcessId pid) {
+ // base::GetParentProcessId() is defined as taking a ProcessHandle instead of
+ // a ProcessId, even though it's a POSIX-only function and IDs and Handles
+ // are both simply pid_t on POSIX... :/
+ base::ProcessHandle handle;
+ CHECK(base::OpenProcessHandle(pid, &handle));
+ base::ProcessId ret = base::GetParentProcessId(pid);
+ base::CloseProcessHandle(handle);
+ return ret;
+}
+
+// SendHello sends a "hello" to socket fd, and then blocks until the recipient
+// acknowledges it by calling RecvHello.
+void SendHello(int fd) {
+ int pipe_fds[2];
+ CHECK_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_pipe(pipe_fds[0]);
+ base::ScopedFD write_pipe(pipe_fds[1]);
+
+ std::vector<int> send_fds;
+ send_fds.push_back(write_pipe.get());
+ CHECK(UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds));
+
+ write_pipe.reset();
+
+ // Block until receiver closes their end of the pipe.
+ char ch;
+ CHECK_EQ(0, HANDLE_EINTR(read(read_pipe.get(), &ch, 1)));
+}
+
+// RecvHello receives and acknowledges a "hello" on socket fd, and returns the
+// process ID of the sender in sender_pid. Optionally, write_pipe can be used
+// to return a file descriptor, and the acknowledgement will be delayed until
+// the descriptor is closed.
+// (Implementation details: SendHello allocates a new pipe, sends us the writing
+// end alongside the "hello" message, and then blocks until we close the writing
+// end of the pipe.)
+void RecvHello(int fd,
+ base::ProcessId* sender_pid,
+ base::ScopedFD* write_pipe = NULL) {
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ ScopedVector<base::ScopedFD> message_fds;
+ ssize_t n = UnixDomainSocket::RecvMsgWithPid(
+ fd, buf, sizeof(buf), &message_fds, sender_pid);
+ CHECK_EQ(sizeof(kHello), static_cast<size_t>(n));
+ CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ CHECK_EQ(1U, message_fds.size());
+ if (write_pipe)
+ write_pipe->swap(*message_fds[0]);
+}
+
+// Check that receiving PIDs works across a fork().
+SANDBOX_TEST(UnixDomainSocketTest, Fork) {
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = fork();
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Similar to Fork above, but forking the child into a new pid namespace.
+SANDBOX_TEST(UnixDomainSocketTest, Namespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ // Check that we think we're pid 1 in our new namespace.
+ CHECK_EQ(1, syscall(__NR_getpid));
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Again similar to Fork, but now with nested PID namespaces.
+SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid2);
+
+ if (pid2 != 0) {
+ // Wait for grandchild to run to completion; see comments below.
+ WaitForExit(pid2);
+
+ // Fallthrough once grandchild has sent its hello and exited.
+ }
+
+ // Check that we think we're pid 1.
+ CHECK_EQ(1, syscall(__NR_getpid));
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ // We have two messages to receive: first from the grand-child,
+ // then from the child.
+ for (unsigned iteration = 0; iteration < 2; ++iteration) {
+ base::ProcessId sender_pid;
+ base::ScopedFD pipe_fd;
+ RecvHello(recv_sock.get(), &sender_pid, &pipe_fd);
+
+ // We need our child and grandchild processes to both be alive for
+ // GetParentProcessId() to return a valid pid, hence the pipe trickery.
+ // (On the first iteration, grandchild is blocked reading from the pipe
+ // until we close it, and child is blocked waiting for grandchild to exit.)
+ switch (iteration) {
+ case 0: // Grandchild's message
+ // Check that sender_pid refers to our grandchild by checking that pid
+ // (our child) is its parent.
+ CHECK_EQ(pid, GetParentProcessId(sender_pid));
+ break;
+ case 1: // Child's message
+ CHECK_EQ(pid, sender_pid);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ WaitForExit(pid);
+}
+
+// Tests that GetPeerPid() returns 0 if the peer does not exist in caller's
+// namespace.
+SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD send_sock(fds[0]);
+ base::ScopedFD recv_sock(fds[1]);
+
+ CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(0, sender_pid);
+ _exit(0);
+ }
+
+ // Parent process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ WaitForExit(pid);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/x86_32_linux_syscalls.h b/sandbox/linux/services/x86_32_linux_syscalls.h
new file mode 100644
index 0000000..0fc2183
--- /dev/null
+++ b/sandbox/linux/services/x86_32_linux_syscalls.h
@@ -0,0 +1,1426 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's syscall_32.tbl.
+#ifndef SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
+
+#if !defined(__i386__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 0
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 1
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork 2
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 3
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 4
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open 5
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 6
+#endif
+
+#if !defined(__NR_waitpid)
+#define __NR_waitpid 7
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat 8
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link 9
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink 10
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 11
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 12
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time 13
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod 14
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod 15
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown 16
+#endif
+
+#if !defined(__NR_break)
+#define __NR_break 17
+#endif
+
+#if !defined(__NR_oldstat)
+#define __NR_oldstat 18
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 19
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 20
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 21
+#endif
+
+#if !defined(__NR_umount)
+#define __NR_umount 22
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 23
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 24
+#endif
+
+#if !defined(__NR_stime)
+#define __NR_stime 25
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 26
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm 27
+#endif
+
+#if !defined(__NR_oldfstat)
+#define __NR_oldfstat 28
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause 29
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime 30
+#endif
+
+#if !defined(__NR_stty)
+#define __NR_stty 31
+#endif
+
+#if !defined(__NR_gtty)
+#define __NR_gtty 32
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access 33
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice 34
+#endif
+
+#if !defined(__NR_ftime)
+#define __NR_ftime 35
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 36
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 37
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename 38
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir 39
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir 40
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 41
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe 42
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 43
+#endif
+
+#if !defined(__NR_prof)
+#define __NR_prof 44
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 45
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 46
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 47
+#endif
+
+#if !defined(__NR_signal)
+#define __NR_signal 48
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 49
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 50
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 51
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 52
+#endif
+
+#if !defined(__NR_lock)
+#define __NR_lock 53
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 54
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 55
+#endif
+
+#if !defined(__NR_mpx)
+#define __NR_mpx 56
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 57
+#endif
+
+#if !defined(__NR_ulimit)
+#define __NR_ulimit 58
+#endif
+
+#if !defined(__NR_oldolduname)
+#define __NR_oldolduname 59
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 60
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 61
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat 62
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 63
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 64
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp 65
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 66
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction 67
+#endif
+
+#if !defined(__NR_sgetmask)
+#define __NR_sgetmask 68
+#endif
+
+#if !defined(__NR_ssetmask)
+#define __NR_ssetmask 69
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 70
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 71
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend 72
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending 73
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 74
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 75
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 76
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 77
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 78
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 79
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 80
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 81
+#endif
+
+#if !defined(__NR_select)
+#define __NR_select 82
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink 83
+#endif
+
+#if !defined(__NR_oldlstat)
+#define __NR_oldlstat 84
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink 85
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib 86
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 87
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 88
+#endif
+
+#if !defined(__NR_readdir)
+#define __NR_readdir 89
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 90
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 91
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 92
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 93
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 94
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 95
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 96
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 97
+#endif
+
+#if !defined(__NR_profil)
+#define __NR_profil 98
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 99
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 100
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm 101
+#endif
+
+#if !defined(__NR_socketcall)
+#define __NR_socketcall 102
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 103
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 104
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 105
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat 106
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat 107
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 108
+#endif
+
+#if !defined(__NR_olduname)
+#define __NR_olduname 109
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl 110
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 111
+#endif
+
+#if !defined(__NR_idle)
+#define __NR_idle 112
+#endif
+
+#if !defined(__NR_vm86old)
+#define __NR_vm86old 113
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 114
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 115
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 116
+#endif
+
+#if !defined(__NR_ipc)
+#define __NR_ipc 117
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 118
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn 119
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 120
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 121
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 122
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt 123
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 124
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 125
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask 126
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module 127
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 128
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 129
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms 130
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 131
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 132
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 133
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush 134
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs 135
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 136
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall 137
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 138
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 139
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek 140
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents 141
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect 142
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 143
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 144
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 145
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 146
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 147
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 148
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl 149
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 150
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 151
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 152
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 153
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 154
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 155
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 156
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 157
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 158
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 159
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 160
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 161
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 162
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 163
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 164
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 165
+#endif
+
+#if !defined(__NR_vm86)
+#define __NR_vm86 166
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module 167
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll 168
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 169
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 170
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 171
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 172
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 173
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 174
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 175
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 176
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 177
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 178
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 179
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 180
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 181
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown 182
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 183
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 184
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 185
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 186
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 187
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg 188
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg 189
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork 190
+#endif
+
+#if !defined(__NR_ugetrlimit)
+#define __NR_ugetrlimit 191
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 192
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 193
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 194
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 195
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 196
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 197
+#endif
+
+#if !defined(__NR_lchown32)
+#define __NR_lchown32 198
+#endif
+
+#if !defined(__NR_getuid32)
+#define __NR_getuid32 199
+#endif
+
+#if !defined(__NR_getgid32)
+#define __NR_getgid32 200
+#endif
+
+#if !defined(__NR_geteuid32)
+#define __NR_geteuid32 201
+#endif
+
+#if !defined(__NR_getegid32)
+#define __NR_getegid32 202
+#endif
+
+#if !defined(__NR_setreuid32)
+#define __NR_setreuid32 203
+#endif
+
+#if !defined(__NR_setregid32)
+#define __NR_setregid32 204
+#endif
+
+#if !defined(__NR_getgroups32)
+#define __NR_getgroups32 205
+#endif
+
+#if !defined(__NR_setgroups32)
+#define __NR_setgroups32 206
+#endif
+
+#if !defined(__NR_fchown32)
+#define __NR_fchown32 207
+#endif
+
+#if !defined(__NR_setresuid32)
+#define __NR_setresuid32 208
+#endif
+
+#if !defined(__NR_getresuid32)
+#define __NR_getresuid32 209
+#endif
+
+#if !defined(__NR_setresgid32)
+#define __NR_setresgid32 210
+#endif
+
+#if !defined(__NR_getresgid32)
+#define __NR_getresgid32 211
+#endif
+
+#if !defined(__NR_chown32)
+#define __NR_chown32 212
+#endif
+
+#if !defined(__NR_setuid32)
+#define __NR_setuid32 213
+#endif
+
+#if !defined(__NR_setgid32)
+#define __NR_setgid32 214
+#endif
+
+#if !defined(__NR_setfsuid32)
+#define __NR_setfsuid32 215
+#endif
+
+#if !defined(__NR_setfsgid32)
+#define __NR_setfsgid32 216
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 217
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 218
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 219
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 220
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 221
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 224
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 225
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 226
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 227
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 228
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 229
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 230
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 231
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 232
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 233
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 234
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 235
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 236
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 237
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 238
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 239
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 240
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 241
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 242
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area 243
+#endif
+
+#if !defined(__NR_get_thread_area)
+#define __NR_get_thread_area 244
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 245
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 246
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 247
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 248
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 249
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 250
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 252
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 253
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create 254
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 255
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait 256
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 257
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 258
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 259
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 260
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 261
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 262
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 263
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 264
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 265
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 266
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 267
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 268
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 269
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 270
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes 271
+#endif
+
+#if !defined(__NR_fadvise64_64)
+#define __NR_fadvise64_64 272
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver 273
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 274
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 275
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 276
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 277
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 278
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 279
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 280
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 281
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 282
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 283
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 284
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 286
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 287
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 288
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 289
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 290
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init 291
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 292
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 293
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 294
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 295
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 296
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 297
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 298
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat 299
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 300
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 301
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 302
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 303
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 304
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 305
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 306
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 307
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 308
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 309
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 310
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 311
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 312
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 313
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 314
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 315
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 316
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 317
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 318
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 319
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 320
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd 321
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 322
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd 323
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 324
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 325
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 326
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 327
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 328
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 329
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 330
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 331
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 332
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 333
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 334
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 335
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 336
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 337
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 338
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 339
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 340
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 341
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 342
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 343
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 344
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 345
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 346
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 347
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 348
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 349
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 350
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 351
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 352
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 353
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 354
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 355
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 356
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/services/x86_64_linux_syscalls.h b/sandbox/linux/services/x86_64_linux_syscalls.h
new file mode 100644
index 0000000..ea6c555
--- /dev/null
+++ b/sandbox/linux/services/x86_64_linux_syscalls.h
@@ -0,0 +1,1294 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's syscall_64.tbl.
+#ifndef SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_
+
+#if !defined(__x86_64__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 0
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 1
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open 2
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 3
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat 4
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 5
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat 6
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll 7
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 8
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 9
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 10
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 11
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 12
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 13
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 14
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 15
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 16
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 17
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 18
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 19
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 20
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access 21
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe 22
+#endif
+
+#if !defined(__NR_select)
+#define __NR_select 23
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 24
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 25
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 26
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 27
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 28
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 29
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 30
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 31
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 32
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 33
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause 34
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 35
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 36
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm 37
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 38
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 39
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 40
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 41
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 42
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept 43
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 44
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 45
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 46
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 47
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 48
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 49
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 50
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 51
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 52
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 53
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 54
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 55
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 56
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork 57
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork 58
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 59
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 60
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 61
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 62
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 63
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 64
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop 65
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 66
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 67
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 68
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 69
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 70
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 71
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 72
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 73
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 74
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 75
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 76
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 77
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents 78
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 79
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 80
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 81
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename 82
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir 83
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir 84
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat 85
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link 86
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink 87
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink 88
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink 89
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod 90
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 91
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown 92
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 93
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown 94
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 95
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 96
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 97
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 98
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 99
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 100
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 101
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 102
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 103
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 104
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 105
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 106
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 107
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 108
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 109
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 110
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp 111
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 112
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 113
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 114
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 115
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 116
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 117
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 118
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 119
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 120
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 121
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 122
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 123
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 124
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 125
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 126
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 127
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 128
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 129
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 130
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 131
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime 132
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod 133
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib 134
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 135
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat 136
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 137
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 138
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs 139
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 140
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 141
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 142
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 143
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 144
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 145
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 146
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 147
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 148
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 149
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 150
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 151
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 152
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 153
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt 154
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 155
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl 156
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 157
+#endif
+
+#if !defined(__NR_arch_prctl)
+#define __NR_arch_prctl 158
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 159
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 160
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 161
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 162
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 163
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 164
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 165
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 166
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 167
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 168
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 169
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 170
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 171
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl 172
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm 173
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module 174
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 175
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 176
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms 177
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module 178
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 179
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 180
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg 181
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg 182
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall 183
+#endif
+
+#if !defined(__NR_tuxcall)
+#define __NR_tuxcall 184
+#endif
+
+#if !defined(__NR_security)
+#define __NR_security 185
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 186
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 187
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 188
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 189
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 190
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 191
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 192
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 193
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 194
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 195
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 196
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 197
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 198
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 199
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 200
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time 201
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 202
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 203
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 204
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area 205
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 206
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 207
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 208
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 209
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 210
+#endif
+
+#if !defined(__NR_get_thread_area)
+#define __NR_get_thread_area 211
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 212
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create 213
+#endif
+
+#if !defined(__NR_epoll_ctl_old)
+#define __NR_epoll_ctl_old 214
+#endif
+
+#if !defined(__NR_epoll_wait_old)
+#define __NR_epoll_wait_old 215
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 216
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 217
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 218
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 219
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop 220
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 221
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 222
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 223
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 224
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 225
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 226
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 227
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 228
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 229
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 230
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 231
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait 232
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 233
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 234
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes 235
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver 236
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 237
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 238
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 239
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 240
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 241
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 242
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 243
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 244
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 245
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 246
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 247
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 248
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 249
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 250
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 251
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 252
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init 253
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 254
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 255
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 256
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 257
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 258
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 259
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 260
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat 261
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat 262
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 263
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 264
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 265
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 266
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 267
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 268
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 269
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 270
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 271
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 272
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 273
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 274
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 275
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 276
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 277
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 278
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 279
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 280
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 281
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd 282
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 283
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd 284
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 285
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 286
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 287
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 288
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 289
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 290
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 291
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 292
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 293
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 294
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 295
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 296
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 297
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 298
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 299
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 300
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 301
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 302
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 303
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 304
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 305
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 306
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 307
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 308
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 309
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 310
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 311
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 312
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 313
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 314
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 315
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 316
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 317
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 318
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 319
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/services/yama.cc b/sandbox/linux/services/yama.cc
new file mode 100644
index 0000000..6658160
--- /dev/null
+++ b/sandbox/linux/services/yama.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/yama.h"
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+#if !defined(PR_SET_PTRACER_ANY)
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// Enable or disable the Yama ptracers restrictions.
+// Return false if Yama is not present on this kernel.
+bool SetYamaPtracersRestriction(bool enable_restrictions) {
+ unsigned long set_ptracer_arg;
+ if (enable_restrictions) {
+ set_ptracer_arg = 0;
+ } else {
+ set_ptracer_arg = PR_SET_PTRACER_ANY;
+ }
+
+ const int ret = prctl(PR_SET_PTRACER, set_ptracer_arg);
+ const int prctl_errno = errno;
+
+ if (0 == ret) {
+ return true;
+ } else {
+ // ENOSYS or EINVAL means Yama is not in the current kernel.
+ CHECK(ENOSYS == prctl_errno || EINVAL == prctl_errno);
+ return false;
+ }
+}
+
+bool CanAccessProcFS() {
+ static const char kProcfsKernelSysPath[] = "/proc/sys/kernel/";
+ int ret = access(kProcfsKernelSysPath, F_OK);
+ if (ret) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+// static
+bool Yama::RestrictPtracersToAncestors() {
+ return SetYamaPtracersRestriction(true /* enable_restrictions */);
+}
+
+// static
+bool Yama::DisableYamaRestrictions() {
+ return SetYamaPtracersRestriction(false /* enable_restrictions */);
+}
+
+// static
+int Yama::GetStatus() {
+ if (!CanAccessProcFS()) {
+ return 0;
+ }
+
+ static const char kPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope";
+
+ base::ScopedFD yama_scope(HANDLE_EINTR(open(kPtraceScopePath, O_RDONLY)));
+
+ if (!yama_scope.is_valid()) {
+ const int open_errno = errno;
+ DCHECK(ENOENT == open_errno);
+ // The status is known, yama is not present.
+ return STATUS_KNOWN;
+ }
+
+ char yama_scope_value = 0;
+ ssize_t num_read = HANDLE_EINTR(read(yama_scope.get(), &yama_scope_value, 1));
+ PCHECK(1 == num_read);
+
+ switch (yama_scope_value) {
+ case '0':
+ return STATUS_KNOWN | STATUS_PRESENT;
+ case '1':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING;
+ case '2':
+ case '3':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING |
+ STATUS_STRICT_ENFORCING;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// static
+bool Yama::IsPresent() { return GetStatus() & STATUS_PRESENT; }
+
+// static
+bool Yama::IsEnforcing() { return GetStatus() & STATUS_ENFORCING; }
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/yama.h b/sandbox/linux/services/yama.h
new file mode 100644
index 0000000..20c28ba
--- /dev/null
+++ b/sandbox/linux/services/yama.h
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_YAMA_H_
+#define SANDBOX_LINUX_SERVICES_YAMA_H_
+
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Yama is a LSM kernel module which can restrict ptrace().
+// This class provides ways to detect if Yama is present and enabled
+// and to restrict which processes can ptrace the current process.
+class SANDBOX_EXPORT Yama {
+ public:
+ // This enum should be used to set or check a bitmask.
+ // A value of 0 would indicate that the status is not known.
+ enum GlobalStatus {
+ STATUS_KNOWN = 1 << 0,
+ STATUS_PRESENT = 1 << 1,
+ STATUS_ENFORCING = 1 << 2,
+ // STATUS_STRICT_ENFORCING corresponds to either mode 2 or mode 3 of Yama.
+ // Ptrace could be entirely denied, or restricted to CAP_SYS_PTRACE
+ // and PTRACE_TRACEME.
+ STATUS_STRICT_ENFORCING = 1 << 3
+ };
+
+ // Restrict who can ptrace() the current process to its ancestors.
+ // If this succeeds, then Yama is available on this kernel.
+ // However, Yama may not be enforcing at this time.
+ static bool RestrictPtracersToAncestors();
+
+ // Disable Yama restrictions for the current process.
+ // This will fail if Yama is not available on this kernel.
+ // This is meant for testing only. If you need this, implement
+ // a per-pid authorization instead.
+ static bool DisableYamaRestrictions();
+
+ // Checks if Yama is currently in enforcing mode for the machine (not the
+ // current process). This requires access to the filesystem and will use
+ // /proc/sys/kernel/yama/ptrace_scope.
+ static int GetStatus();
+
+ // Helper for checking for STATUS_PRESENT in GetStatus().
+ static bool IsPresent();
+ // Helper for checkking for STATUS_ENFORCING in GetStatus().
+ static bool IsEnforcing();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Yama);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_YAMA_H_
diff --git a/sandbox/linux/services/yama_unittests.cc b/sandbox/linux/services/yama_unittests.cc
new file mode 100644
index 0000000..a4100a6
--- /dev/null
+++ b/sandbox/linux/services/yama_unittests.cc
@@ -0,0 +1,172 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/services/yama.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool HasLinux32Bug() {
+#if defined(__i386__)
+ // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels.
+ // This is fixed in 3.4.
+ bool is_kernel_64bit =
+ base::SysInfo::OperatingSystemArchitecture() == "x86_64";
+ bool is_linux = base::SysInfo::OperatingSystemName() == "Linux";
+ bool is_3_dot_2 = StartsWithASCII(
+ base::SysInfo::OperatingSystemVersion(), "3.2", /*case_sensitive=*/false);
+ if (is_kernel_64bit && is_linux && is_3_dot_2)
+ return true;
+#endif // defined(__i386__)
+ return false;
+}
+
+bool CanPtrace(pid_t pid) {
+ int ret;
+ ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
+ if (ret == -1) {
+ CHECK_EQ(EPERM, errno);
+ return false;
+ }
+ // Wait for the process to be stopped so that it can be detached.
+ siginfo_t process_info;
+ int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
+ PCHECK(0 == wait_ret);
+ PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
+ return true;
+}
+
+// _exit(0) if pid can be ptraced by the current process.
+// _exit(1) otherwise.
+void ExitZeroIfCanPtrace(pid_t pid) {
+ if (CanPtrace(pid)) {
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+}
+
+bool CanSubProcessPtrace(pid_t pid) {
+ ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
+ bool signaled;
+ int exit_code = process.WaitForExit(&signaled);
+ CHECK(!signaled);
+ return 0 == exit_code;
+}
+
+// The tests below assume that the system-level configuration will not change
+// while they run.
+
+TEST(Yama, GetStatus) {
+ int status1 = Yama::GetStatus();
+
+ // Check that the value is a possible bitmask.
+ ASSERT_LE(0, status1);
+ ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
+ Yama::STATUS_STRICT_ENFORCING,
+ status1);
+
+ // The status should not just be a random value.
+ int status2 = Yama::GetStatus();
+ EXPECT_EQ(status1, status2);
+
+ // This test is not running sandboxed, there is no reason to not know the
+ // status.
+ EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
+
+ if (status1 & Yama::STATUS_STRICT_ENFORCING) {
+ // If Yama is strictly enforcing, it is also enforcing.
+ EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
+ }
+
+ if (status1 & Yama::STATUS_ENFORCING) {
+ // If Yama is enforcing, Yama is present.
+ EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
+ }
+
+ // Verify that the helper functions work as intended.
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
+ Yama::IsEnforcing());
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
+ Yama::IsPresent());
+
+ fprintf(stdout,
+ "Yama present: %s - enforcing: %s\n",
+ Yama::IsPresent() ? "Y" : "N",
+ Yama::IsEnforcing() ? "Y" : "N");
+}
+
+SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
+ // This call will succeed iff Yama is present.
+ bool restricted = Yama::RestrictPtracersToAncestors();
+ CHECK_EQ(restricted, Yama::IsPresent());
+}
+
+// Attempts to enable or disable Yama restrictions.
+void SetYamaRestrictions(bool enable_restriction) {
+ if (enable_restriction) {
+ Yama::RestrictPtracersToAncestors();
+ } else {
+ Yama::DisableYamaRestrictions();
+ }
+}
+
+TEST(Yama, RestrictPtraceWorks) {
+ if (HasLinux32Bug())
+ return;
+
+ ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
+ ASSERT_TRUE(process1.WaitForClosureToRun());
+
+ if (Yama::IsEnforcing()) {
+ // A sibling process cannot ptrace process1.
+ ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
+ }
+
+ if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
+ // However, parent can ptrace process1.
+ ASSERT_TRUE(CanPtrace(process1.GetPid()));
+
+ // A sibling can ptrace process2 which disables any Yama protection.
+ ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
+ ASSERT_TRUE(process2.WaitForClosureToRun());
+ ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
+ }
+}
+
+void DoNothing() {}
+
+SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
+ if (!Yama::IsPresent() || HasLinux32Bug())
+ return;
+
+ CHECK(Yama::DisableYamaRestrictions());
+ ScopedProcess process1(base::Bind(&DoNothing));
+
+ if (Yama::IsEnforcing()) {
+ // Check that process1 is protected by Yama, even though it has
+ // been created from a process that disabled Yama.
+ CHECK(!CanSubProcessPtrace(process1.GetPid()));
+ }
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/suid/client/DEPS b/sandbox/linux/suid/client/DEPS
new file mode 100644
index 0000000..99a337d
--- /dev/null
+++ b/sandbox/linux/suid/client/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/services",
+]
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc
new file mode 100644
index 0000000..f0b5cef
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/services/init_process_reaper.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
+
+namespace {
+
+bool IsFileSystemAccessDenied() {
+ base::ScopedFD self_exe(HANDLE_EINTR(open(base::kProcSelfExe, O_RDONLY)));
+ return !self_exe.is_valid();
+}
+
+// Set an environment variable that reflects the API version we expect from the
+// setuid sandbox. Old versions of the sandbox will ignore this.
+void SetSandboxAPIEnvironmentVariable(base::Environment* env) {
+ env->SetVar(sandbox::kSandboxEnvironmentApiRequest,
+ base::IntToString(sandbox::kSUIDSandboxApiNumber));
+}
+
+// Unset environment variables that are expected to be set by the setuid
+// sandbox. This is to allow nesting of one instance of the SUID sandbox
+// inside another.
+void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) {
+ DCHECK(env_map);
+ const base::NativeEnvironmentString environment_vars[] = {
+ sandbox::kSandboxDescriptorEnvironmentVarName,
+ sandbox::kSandboxHelperPidEnvironmentVarName,
+ sandbox::kSandboxEnvironmentApiProvides,
+ sandbox::kSandboxPIDNSEnvironmentVarName,
+ sandbox::kSandboxNETNSEnvironmentVarName,
+ };
+
+ for (size_t i = 0; i < arraysize(environment_vars); ++i) {
+ // Setting values in EnvironmentMap to an empty-string will make
+ // sure that they get unset from the environment via AlterEnvironment().
+ (*env_map)[environment_vars[i]] = base::NativeEnvironmentString();
+ }
+}
+
+// Wrapper around a shared C function.
+// Returns the "saved" environment variable name corresponding to |envvar|
+// in a new string or NULL.
+std::string* CreateSavedVariableName(const char* env_var) {
+ char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var);
+ if (!saved_env_var)
+ return NULL;
+ std::string* saved_env_var_copy = new std::string(saved_env_var);
+ // SandboxSavedEnvironmentVariable is the C function that we wrap and uses
+ // malloc() to allocate memory.
+ free(saved_env_var);
+ return saved_env_var_copy;
+}
+
+// The ELF loader will clear many environment variables so we save them to
+// different names here so that the SUID sandbox can resolve them for the
+// renderer.
+void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) {
+ for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+ const char* env_var = kSUIDUnsafeEnvironmentVariables[i];
+ // Get the saved environment variable corresponding to envvar.
+ scoped_ptr<std::string> saved_env_var(CreateSavedVariableName(env_var));
+ if (saved_env_var == NULL)
+ continue;
+
+ std::string value;
+ if (env->GetVar(env_var, &value))
+ env->SetVar(saved_env_var->c_str(), value);
+ else
+ env->UnSetVar(saved_env_var->c_str());
+ }
+}
+
+int GetHelperApi(base::Environment* env) {
+ std::string api_string;
+ int api_number = 0; // Assume API version 0 if no environment was found.
+ if (env->GetVar(sandbox::kSandboxEnvironmentApiProvides, &api_string) &&
+ !base::StringToInt(api_string, &api_number)) {
+ // It's an error if we could not convert the API number.
+ api_number = -1;
+ }
+ return api_number;
+}
+
+// Convert |var_name| from the environment |env| to an int.
+// Return -1 if the variable does not exist or the value cannot be converted.
+int EnvToInt(base::Environment* env, const char* var_name) {
+ std::string var_string;
+ int var_value = -1;
+ if (env->GetVar(var_name, &var_string) &&
+ !base::StringToInt(var_string, &var_value)) {
+ var_value = -1;
+ }
+ return var_value;
+}
+
+pid_t GetHelperPID(base::Environment* env) {
+ return EnvToInt(env, sandbox::kSandboxHelperPidEnvironmentVarName);
+}
+
+// Get the IPC file descriptor used to communicate with the setuid helper.
+int GetIPCDescriptor(base::Environment* env) {
+ return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName);
+}
+
+const char* GetDevelSandboxPath() {
+ return getenv("CHROME_DEVEL_SANDBOX");
+}
+
+} // namespace
+
+namespace sandbox {
+
+SetuidSandboxClient* SetuidSandboxClient::Create() {
+ base::Environment* environment(base::Environment::Create());
+ SetuidSandboxClient* sandbox_client(new SetuidSandboxClient);
+
+ CHECK(environment);
+ sandbox_client->env_ = environment;
+ return sandbox_client;
+}
+
+SetuidSandboxClient::SetuidSandboxClient()
+ : env_(NULL),
+ sandboxed_(false) {
+}
+
+SetuidSandboxClient::~SetuidSandboxClient() {
+ delete env_;
+}
+
+void SetuidSandboxClient::CloseDummyFile() {
+ // When we're launched through the setuid sandbox, SetupLaunchOptions
+ // arranges for kZygoteIdFd to be a dummy file descriptor to satisfy an
+ // ancient setuid sandbox ABI requirement. However, the descriptor is no
+ // longer needed, so we can simply close it right away now.
+ CHECK(IsSuidSandboxChild());
+
+ // Sanity check that kZygoteIdFd refers to a pipe.
+ struct stat st;
+ PCHECK(0 == fstat(kZygoteIdFd, &st));
+ CHECK(S_ISFIFO(st.st_mode));
+
+ PCHECK(0 == IGNORE_EINTR(close(kZygoteIdFd)));
+}
+
+bool SetuidSandboxClient::ChrootMe() {
+ int ipc_fd = GetIPCDescriptor(env_);
+
+ if (ipc_fd < 0) {
+ LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor";
+ return false;
+ }
+
+ if (HANDLE_EINTR(write(ipc_fd, &kMsgChrootMe, 1)) != 1) {
+ PLOG(ERROR) << "Failed to write to chroot pipe";
+ return false;
+ }
+
+ // We need to reap the chroot helper process in any event.
+ pid_t helper_pid = GetHelperPID(env_);
+ // If helper_pid is -1 we wait for any child.
+ if (HANDLE_EINTR(waitpid(helper_pid, NULL, 0)) < 0) {
+ PLOG(ERROR) << "Failed to wait for setuid helper to die";
+ return false;
+ }
+
+ char reply;
+ if (HANDLE_EINTR(read(ipc_fd, &reply, 1)) != 1) {
+ PLOG(ERROR) << "Failed to read from chroot pipe";
+ return false;
+ }
+
+ if (reply != kMsgChrootSuccessful) {
+ LOG(ERROR) << "Error code reply from chroot helper";
+ return false;
+ }
+
+ // We now consider ourselves "fully sandboxed" as far as the
+ // setuid sandbox is concerned.
+ CHECK(IsFileSystemAccessDenied());
+ sandboxed_ = true;
+ return true;
+}
+
+bool SetuidSandboxClient::CreateInitProcessReaper(
+ base::Closure* post_fork_parent_callback) {
+ return sandbox::CreateInitProcessReaper(post_fork_parent_callback);
+}
+
+bool SetuidSandboxClient::IsSuidSandboxUpToDate() const {
+ return GetHelperApi(env_) == kSUIDSandboxApiNumber;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxChild() const {
+ return GetIPCDescriptor(env_) >= 0;
+}
+
+bool SetuidSandboxClient::IsInNewPIDNamespace() const {
+ return env_->HasVar(kSandboxPIDNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsInNewNETNamespace() const {
+ return env_->HasVar(kSandboxNETNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsSandboxed() const {
+ return sandboxed_;
+}
+
+// Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables
+// the setuid sandbox. TODO(jln): fix this (crbug.com/245376).
+bool SetuidSandboxClient::IsDisabledViaEnvironment() {
+ const char* devel_sandbox_path = GetDevelSandboxPath();
+ if (devel_sandbox_path && '\0' == *devel_sandbox_path) {
+ return true;
+ }
+ return false;
+}
+
+base::FilePath SetuidSandboxClient::GetSandboxBinaryPath() {
+ base::FilePath sandbox_binary;
+ base::FilePath exe_dir;
+ if (PathService::Get(base::DIR_EXE, &exe_dir)) {
+ base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
+ if (base::PathExists(sandbox_candidate))
+ sandbox_binary = sandbox_candidate;
+ }
+
+ // In user-managed builds, including development builds, an environment
+ // variable is required to enable the sandbox. See
+ // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
+ struct stat st;
+ if (sandbox_binary.empty() && stat(base::kProcSelfExe, &st) == 0 &&
+ st.st_uid == getuid()) {
+ const char* devel_sandbox_path = GetDevelSandboxPath();
+ if (devel_sandbox_path) {
+ sandbox_binary = base::FilePath(devel_sandbox_path);
+ }
+ }
+
+ return sandbox_binary;
+}
+
+void SetuidSandboxClient::PrependWrapper(base::CommandLine* cmd_line) {
+ std::string sandbox_binary(GetSandboxBinaryPath().value());
+ struct stat st;
+ if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) {
+ LOG(FATAL) << "The SUID sandbox helper binary is missing: "
+ << sandbox_binary << " Aborting now. See "
+ "https://code.google.com/p/chromium/wiki/"
+ "LinuxSUIDSandboxDevelopment.";
+ }
+
+ if (access(sandbox_binary.c_str(), X_OK) != 0 || (st.st_uid != 0) ||
+ ((st.st_mode & S_ISUID) == 0) || ((st.st_mode & S_IXOTH)) == 0) {
+ LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
+ "configured correctly. Rather than run without sandboxing "
+ "I'm aborting now. You need to make sure that "
+ << sandbox_binary << " is owned by root and has mode 4755.";
+ }
+
+ cmd_line->PrependWrapper(sandbox_binary);
+}
+
+void SetuidSandboxClient::SetupLaunchOptions(
+ base::LaunchOptions* options,
+ base::FileHandleMappingVector* fds_to_remap,
+ base::ScopedFD* dummy_fd) {
+ DCHECK(options);
+ DCHECK(fds_to_remap);
+
+ // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used.
+ options->allow_new_privs = true;
+ UnsetExpectedEnvironmentVariables(&options->environ);
+
+ // Set dummy_fd to the reading end of a closed pipe.
+ int pipe_fds[2];
+ PCHECK(0 == pipe(pipe_fds));
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1])));
+ dummy_fd->reset(pipe_fds[0]);
+
+ // We no longer need a dummy socket for discovering the child's PID,
+ // but the sandbox is still hard-coded to expect a file descriptor at
+ // kZygoteIdFd. Fixing this requires a sandbox API change. :(
+ fds_to_remap->push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd));
+}
+
+void SetuidSandboxClient::SetupLaunchEnvironment() {
+ SaveSUIDUnsafeEnvironmentVariables(env_);
+ SetSandboxAPIEnvironmentVariable(env_);
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h
new file mode 100644
index 0000000..e6a3e4c
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/process/launch.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Helper class to use the setuid sandbox. This class is to be used both
+// before launching the setuid helper and after being executed through the
+// setuid helper.
+// This class is difficult to use. It has been created by refactoring very old
+// code scathered through the Chromium code base.
+//
+// A typical use for "A" launching a sandboxed process "B" would be:
+// 1. A calls SetupLaunchEnvironment()
+// 2. A sets up a CommandLine and then amends it with
+// PrependWrapper() (or manually, by relying on GetSandboxBinaryPath()).
+// 3. A uses SetupLaunchOptions() to arrange for a dummy descriptor for the
+// setuid sandbox ABI.
+// 4. A launches B with base::LaunchProcess, using the amended CommandLine.
+// 5. B uses CloseDummyFile() to close the dummy file descriptor.
+// 6. B performs various initializations that require access to the file
+// system.
+// 6.b (optional) B uses sandbox::Credentials::HasOpenDirectory() to verify
+// that no directory is kept open (which would allow bypassing the setuid
+// sandbox).
+// 7. B should be prepared to assume the role of init(1). In particular, B
+// cannot receive any signal from any other process, excluding SIGKILL.
+// If B dies, all the processes in the namespace will die.
+// B can fork() and the parent can assume the role of init(1), by using
+// CreateInitProcessReaper().
+// 8. B requests being chroot-ed through ChrootMe() and
+// requests other sandboxing status via the status functions.
+class SANDBOX_EXPORT SetuidSandboxClient {
+ public:
+ // All instantation should go through this factory method.
+ static class SetuidSandboxClient* Create();
+ ~SetuidSandboxClient();
+
+ // Close the dummy file descriptor leftover from the sandbox ABI.
+ void CloseDummyFile();
+ // Ask the setuid helper over the setuid sandbox IPC channel to chroot() us
+ // to an empty directory.
+ // Will only work if we have been launched through the setuid helper.
+ bool ChrootMe();
+ // When a new PID namespace is created, the process with pid == 1 should
+ // assume the role of init.
+ // See sandbox/linux/services/init_process_reaper.h for more information
+ // on this API.
+ bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback);
+
+ // Did we get launched through an up to date setuid binary ?
+ bool IsSuidSandboxUpToDate() const;
+ // Did we get launched through the setuid helper ?
+ bool IsSuidSandboxChild() const;
+ // Did the setuid helper create a new PID namespace ?
+ bool IsInNewPIDNamespace() const;
+ // Did the setuid helper create a new network namespace ?
+ bool IsInNewNETNamespace() const;
+ // Are we done and fully sandboxed ?
+ bool IsSandboxed() const;
+
+ // The setuid sandbox may still be disabled via the environment.
+ // This is tracked in crbug.com/245376.
+ bool IsDisabledViaEnvironment();
+ // Get the sandbox binary path. This method knows about the
+ // CHROME_DEVEL_SANDBOX environment variable used for user-managed builds. If
+ // the sandbox binary cannot be found, it will return an empty FilePath.
+ base::FilePath GetSandboxBinaryPath();
+ // Modify |cmd_line| to launch via the setuid sandbox. Crash if the setuid
+ // sandbox binary cannot be found. |cmd_line| must not be NULL.
+ void PrependWrapper(base::CommandLine* cmd_line);
+ // Set-up the launch options for launching via the setuid sandbox. Caller is
+ // responsible for keeping |dummy_fd| alive until LaunchProcess() completes.
+ // |options| and |fds_to_remap| must not be NULL.
+ // (Keeping |dummy_fd| alive is an unfortunate historical artifact of the
+ // chrome-sandbox ABI.)
+ void SetupLaunchOptions(base::LaunchOptions* options,
+ base::FileHandleMappingVector* fds_to_remap,
+ base::ScopedFD* dummy_fd);
+ // Set-up the environment. This should be done prior to launching the setuid
+ // helper.
+ void SetupLaunchEnvironment();
+
+ private:
+ SetuidSandboxClient();
+
+ // Holds the environment. Will never be NULL.
+ base::Environment* env_;
+ bool sandboxed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetuidSandboxClient);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
new file mode 100644
index 0000000..d4f7dfe
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(SetuidSandboxClient, SetupLaunchEnvironment) {
+ const char kTestValue[] = "This is a test";
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ EXPECT_TRUE(env != NULL);
+
+ std::string saved_ld_preload;
+ bool environment_had_ld_preload;
+ // First, back-up the real LD_PRELOAD if any.
+ environment_had_ld_preload = env->GetVar("LD_PRELOAD", &saved_ld_preload);
+ // Setup environment variables to save or not save.
+ EXPECT_TRUE(env->SetVar("LD_PRELOAD", kTestValue));
+ EXPECT_TRUE(env->UnSetVar("LD_ORIGIN_PATH"));
+
+ scoped_ptr<SetuidSandboxClient>
+ sandbox_client(SetuidSandboxClient::Create());
+ EXPECT_TRUE(sandbox_client != NULL);
+
+ // Make sure the environment is clean.
+ EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiRequest));
+ EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiProvides));
+
+ sandbox_client->SetupLaunchEnvironment();
+
+ // Check if the requested API environment was set.
+ std::string api_request;
+ EXPECT_TRUE(env->GetVar(kSandboxEnvironmentApiRequest, &api_request));
+ int api_request_num;
+ EXPECT_TRUE(base::StringToInt(api_request, &api_request_num));
+ EXPECT_EQ(api_request_num, kSUIDSandboxApiNumber);
+
+ // Now check if LD_PRELOAD was saved to SANDBOX_LD_PRELOAD.
+ std::string sandbox_ld_preload;
+ EXPECT_TRUE(env->GetVar("SANDBOX_LD_PRELOAD", &sandbox_ld_preload));
+ EXPECT_EQ(sandbox_ld_preload, kTestValue);
+
+ // Check that LD_ORIGIN_PATH was not saved.
+ EXPECT_FALSE(env->HasVar("SANDBOX_LD_ORIGIN_PATH"));
+
+ // We should not forget to restore LD_PRELOAD at the end, or this environment
+ // variable will affect the next running tests!
+ if (environment_had_ld_preload) {
+ EXPECT_TRUE(env->SetVar("LD_PRELOAD", saved_ld_preload));
+ } else {
+ EXPECT_TRUE(env->UnSetVar("LD_PRELOAD"));
+ }
+}
+
+TEST(SetuidSandboxClient, SandboxedClientAPI) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ EXPECT_TRUE(env != NULL);
+
+ scoped_ptr<SetuidSandboxClient>
+ sandbox_client(SetuidSandboxClient::Create());
+ EXPECT_TRUE(sandbox_client != NULL);
+
+ // Set-up a fake environment as if we went through the setuid sandbox.
+ EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
+ base::IntToString(kSUIDSandboxApiNumber)));
+ EXPECT_TRUE(env->SetVar(kSandboxDescriptorEnvironmentVarName, "1"));
+ EXPECT_TRUE(env->SetVar(kSandboxPIDNSEnvironmentVarName, "1"));
+ EXPECT_TRUE(env->UnSetVar(kSandboxNETNSEnvironmentVarName));
+
+ // Check the API.
+ EXPECT_TRUE(sandbox_client->IsSuidSandboxUpToDate());
+ EXPECT_TRUE(sandbox_client->IsSuidSandboxChild());
+ EXPECT_TRUE(sandbox_client->IsInNewPIDNamespace());
+ EXPECT_FALSE(sandbox_client->IsInNewNETNamespace());
+
+ // Forge an incorrect API version and check.
+ EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
+ base::IntToString(kSUIDSandboxApiNumber + 1)));
+ EXPECT_FALSE(sandbox_client->IsSuidSandboxUpToDate());
+ // We didn't go through the actual sandboxing mechanism as it is
+ // very hard in a unit test.
+ EXPECT_FALSE(sandbox_client->IsSandboxed());
+}
+
+// This test doesn't accomplish much, but will make sure that analysis tools
+// will run this codepath.
+TEST(SetuidSandboxClient, GetSandboxBinaryPath) {
+ scoped_ptr<SetuidSandboxClient> setuid_sandbox_client(
+ SetuidSandboxClient::Create());
+ ignore_result(setuid_sandbox_client->GetSandboxBinaryPath());
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/linux/suid/common/sandbox.h b/sandbox/linux/suid/common/sandbox.h
new file mode 100644
index 0000000..99eb7b5
--- /dev/null
+++ b/sandbox/linux/suid/common/sandbox.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SUID_SANDBOX_H_
+#define SANDBOX_LINUX_SUID_SANDBOX_H_
+
+#if defined(__cplusplus)
+namespace sandbox {
+#endif
+
+// These are command line switches that may be used by other programs
+// (e.g. Chrome) to construct a command line for the sandbox.
+static const char kSuidSandboxGetApiSwitch[] = "--get-api";
+static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score";
+
+static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D";
+static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID";
+
+static const long kSUIDSandboxApiNumber = 1;
+static const char kSandboxEnvironmentApiRequest[] = "SBX_CHROME_API_RQ";
+static const char kSandboxEnvironmentApiProvides[] = "SBX_CHROME_API_PRV";
+
+// This number must be kept in sync with common/zygote_commands_linux.h
+static const int kZygoteIdFd = 7;
+
+// These are the magic byte values which the sandboxed process uses to request
+// that it be chrooted.
+static const char kMsgChrootMe = 'C';
+static const char kMsgChrootSuccessful = 'O';
+
+// These are set if we have respectively switched to a new PID or NET namespace
+// by going through the setuid binary helper.
+static const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS";
+static const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
+
+#if defined(__cplusplus)
+} // namespace sandbox
+#endif
+
+#endif // SANDBOX_LINUX_SUID_SANDBOX_H_
diff --git a/sandbox/linux/suid/common/suid_unsafe_environment_variables.h b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
new file mode 100644
index 0000000..33ba4b6
--- /dev/null
+++ b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a list of environment variables which the ELF loader unsets when
+// loading a SUID binary. Because they are unset rather than just ignored, they
+// aren't passed to child processes of SUID processes either.
+//
+// We need to save these environment variables before running a SUID sandbox
+// and restore them before running child processes (but after dropping root).
+//
+// List gathered from glibc sources (00ebd7ed58df389a78e41dece058048725cb585e):
+// sysdeps/unix/sysv/linux/i386/dl-librecon.h
+// sysdeps/generic/unsecvars.h
+
+#ifndef SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
+#define SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
+
+#include <stdint.h>
+#include <stdlib.h> // malloc
+#include <string.h> // memcpy
+
+static const char* kSUIDUnsafeEnvironmentVariables[] = {
+ "LD_AOUT_LIBRARY_PATH",
+ "LD_AOUT_PRELOAD",
+ "GCONV_PATH",
+ "GETCONF_DIR",
+ "HOSTALIASES",
+ "LD_AUDIT",
+ "LD_DEBUG",
+ "LD_DEBUG_OUTPUT",
+ "LD_DYNAMIC_WEAK",
+ "LD_LIBRARY_PATH",
+ "LD_ORIGIN_PATH",
+ "LD_PRELOAD",
+ "LD_PROFILE",
+ "LD_SHOW_AUXV",
+ "LD_USE_LOAD_BIAS",
+ "LOCALDOMAIN",
+ "LOCPATH",
+ "MALLOC_TRACE",
+ "NIS_PATH",
+ "NLSPATH",
+ "RESOLV_HOST_CONF",
+ "RES_OPTIONS",
+ "TMPDIR",
+ "TZDIR",
+ NULL,
+};
+
+// Return a malloc allocated string containing the 'saved' environment variable
+// name for a given environment variable.
+static inline char* SandboxSavedEnvironmentVariable(const char* envvar) {
+ const size_t envvar_len = strlen(envvar);
+ const size_t kMaxSizeT = (size_t) -1;
+
+ if (envvar_len > kMaxSizeT - 1 - 8)
+ return NULL;
+
+ const size_t saved_envvarlen = envvar_len + 1 /* NUL terminator */ +
+ 8 /* strlen("SANDBOX_") */;
+ char* const saved_envvar = (char*) malloc(saved_envvarlen);
+ if (!saved_envvar)
+ return NULL;
+
+ memcpy(saved_envvar, "SANDBOX_", 8);
+ memcpy(saved_envvar + 8, envvar, envvar_len);
+ saved_envvar[8 + envvar_len] = 0;
+
+ return saved_envvar;
+}
+
+#endif // SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
diff --git a/sandbox/linux/suid/process_util.h b/sandbox/linux/suid/process_util.h
new file mode 100644
index 0000000..9fb9a87
--- /dev/null
+++ b/sandbox/linux/suid/process_util.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is duplicated from base/process_utils.h.
+// We shouldn't link against C++ code in a setuid binary.
+
+#ifndef SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
+#define SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+// This adjusts /proc/process/oom_score_adj so the Linux OOM killer
+// will prefer certain process types over others. The range for the
+// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
+//
+// If the Linux system isn't new enough to use oom_score_adj, then we
+// try to set the older oom_adj value instead, scaling the score to
+// the required range of [0, 15]. This may result in some aliasing of
+// values, of course.
+bool AdjustOOMScore(pid_t process, int score);
+
+// This adjusts /sys/kernel/mm/chromeos-low_mem/margin so that
+// the kernel notifies us that we are low on memory when less than
+// |margin_mb| megabytes are available. Setting |margin_mb| to -1
+// turns off low memory notification.
+bool AdjustLowMemoryMargin(int64_t margin_mb);
+
+#endif // SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
diff --git a/sandbox/linux/suid/process_util_linux.c b/sandbox/linux/suid/process_util_linux.c
new file mode 100644
index 0000000..8d9a53c
--- /dev/null
+++ b/sandbox/linux/suid/process_util_linux.c
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is the C version of code from base/process_utils_linux.cc.
+// We shouldn't link against C++ code in a setuid binary.
+
+// Needed for O_DIRECTORY, must be defined before fcntl.h is included
+// (and it can be included earlier than the explicit #include below
+// in some versions of glibc).
+#define _GNU_SOURCE
+
+#include "sandbox/linux/suid/process_util.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Ranges for the current (oom_score_adj) and previous (oom_adj)
+// flavors of OOM score.
+static const int kMaxOomScore = 1000;
+static const int kMaxOldOomScore = 15;
+
+// NOTE: This is not the only version of this function in the source:
+// the base library (in process_util_linux.cc) also has its own C++ version.
+bool AdjustOOMScore(pid_t process, int score) {
+ if (score < 0 || score > kMaxOomScore)
+ return false;
+
+ char oom_adj[27]; // "/proc/" + log_10(2**64) + "\0"
+ // 6 + 20 + 1 = 27
+ snprintf(oom_adj, sizeof(oom_adj), "/proc/%" PRIdMAX, (intmax_t)process);
+
+ const int dirfd = open(oom_adj, O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0)
+ return false;
+
+ struct stat statbuf;
+ if (fstat(dirfd, &statbuf) < 0) {
+ close(dirfd);
+ return false;
+ }
+ if (getuid() != statbuf.st_uid) {
+ close(dirfd);
+ return false;
+ }
+
+ int fd = openat(dirfd, "oom_score_adj", O_WRONLY);
+ if (fd < 0) {
+ // We failed to open oom_score_adj, so let's try for the older
+ // oom_adj file instead.
+ fd = openat(dirfd, "oom_adj", O_WRONLY);
+ if (fd < 0) {
+ // Nope, that doesn't work either.
+ return false;
+ } else {
+ // If we're using the old oom_adj file, the allowed range is now
+ // [0, kMaxOldOomScore], so we scale the score. This may result in some
+ // aliasing of values, of course.
+ score = score * kMaxOldOomScore / kMaxOomScore;
+ }
+ }
+ close(dirfd);
+
+ char buf[11]; // 0 <= |score| <= kMaxOomScore; using log_10(2**32) + 1 size
+ snprintf(buf, sizeof(buf), "%d", score);
+ size_t len = strlen(buf);
+
+ ssize_t bytes_written = write(fd, buf, len);
+ close(fd);
+ return (bytes_written == (ssize_t)len);
+}
diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c
new file mode 100644
index 0000000..3049ae5
--- /dev/null
+++ b/sandbox/linux/suid/sandbox.c
@@ -0,0 +1,480 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
+
+#include "sandbox/linux/suid/common/sandbox.h"
+
+#define _GNU_SOURCE
+#include <asm/unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
+#include "sandbox/linux/suid/process_util.h"
+
+#if !defined(CLONE_NEWPID)
+#define CLONE_NEWPID 0x20000000
+#endif
+#if !defined(CLONE_NEWNET)
+#define CLONE_NEWNET 0x40000000
+#endif
+
+static bool DropRoot();
+
+#define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x)
+
+static void FatalError(const char* msg, ...)
+ __attribute__((noreturn, format(printf, 1, 2)));
+
+static void FatalError(const char* msg, ...) {
+ va_list ap;
+ va_start(ap, msg);
+
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, ": %s\n", strerror(errno));
+ fflush(stderr);
+ va_end(ap);
+ _exit(1);
+}
+
+static void ExitWithErrorSignalHandler(int signal) {
+ const char msg[] = "\nThe setuid sandbox got signaled, exiting.\n";
+ if (-1 == write(2, msg, sizeof(msg) - 1)) {
+ // Do nothing.
+ }
+
+ _exit(1);
+}
+
+// We will chroot() to the helper's /proc/self directory. Anything there will
+// not exist anymore if we make sure to wait() for the helper.
+//
+// /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty
+// even if the helper survives as a zombie.
+//
+// There is very little reason to use fdinfo/ instead of fd/ but we are
+// paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/
+#define SAFE_DIR "/proc/self/fdinfo"
+#define SAFE_DIR2 "/proc/self/fd"
+
+static bool SpawnChrootHelper() {
+ int sv[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+ perror("socketpair");
+ return false;
+ }
+
+ char* safedir = NULL;
+ struct stat sdir_stat;
+ if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) {
+ safedir = SAFE_DIR;
+ } else if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) {
+ safedir = SAFE_DIR2;
+ } else {
+ fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
+ return false;
+ }
+
+ const pid_t pid = syscall(__NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
+
+ if (pid == -1) {
+ perror("clone");
+ close(sv[0]);
+ close(sv[1]);
+ return false;
+ }
+
+ if (pid == 0) {
+ // We share our files structure with an untrusted process. As a security in
+ // depth measure, we make sure that we can't open anything by mistake.
+ // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
+
+ const struct rlimit nofile = {0, 0};
+ if (setrlimit(RLIMIT_NOFILE, &nofile))
+ FatalError("Setting RLIMIT_NOFILE");
+
+ if (close(sv[1]))
+ FatalError("close");
+
+ // wait for message
+ char msg;
+ ssize_t bytes;
+ do {
+ bytes = read(sv[0], &msg, 1);
+ } while (bytes == -1 && errno == EINTR);
+
+ if (bytes == 0)
+ _exit(0);
+ if (bytes != 1)
+ FatalError("read");
+
+ // do chrooting
+ if (msg != kMsgChrootMe)
+ FatalError("Unknown message from sandboxed process");
+
+ // sanity check
+ if (chdir(safedir))
+ FatalError("Cannot chdir into /proc/ directory");
+
+ if (chroot(safedir))
+ FatalError("Cannot chroot into /proc/ directory");
+
+ if (chdir("/"))
+ FatalError("Cannot chdir to / after chroot");
+
+ const char reply = kMsgChrootSuccessful;
+ do {
+ bytes = write(sv[0], &reply, 1);
+ } while (bytes == -1 && errno == EINTR);
+
+ if (bytes != 1)
+ FatalError("Writing reply");
+
+ _exit(0);
+ // We now become a zombie. /proc/self/fd(info) is now an empty dir and we
+ // are chrooted there.
+ // Our (unprivileged) parent should not even be able to open "." or "/"
+ // since they would need to pass the ptrace() check. If our parent wait()
+ // for us, our root directory will completely disappear.
+ }
+
+ if (close(sv[0])) {
+ close(sv[1]);
+ perror("close");
+ return false;
+ }
+
+ // In the parent process, we install an environment variable containing the
+ // number of the file descriptor.
+ char desc_str[64];
+ int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
+ if (printed < 0 || printed >= (int)sizeof(desc_str)) {
+ fprintf(stderr, "Failed to snprintf\n");
+ return false;
+ }
+
+ if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
+ perror("setenv");
+ close(sv[1]);
+ return false;
+ }
+
+ // We also install an environment variable containing the pid of the child
+ char helper_pid_str[64];
+ printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
+ if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
+ fprintf(stderr, "Failed to snprintf\n");
+ return false;
+ }
+
+ if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
+ perror("setenv");
+ close(sv[1]);
+ return false;
+ }
+
+ return true;
+}
+
+// Block until child_pid exits, then exit. Try to preserve the exit code.
+static void WaitForChildAndExit(pid_t child_pid) {
+ int exit_code = -1;
+ siginfo_t reaped_child_info;
+
+ // Don't "Core" on SIGABRT. SIGABRT is sent by the Chrome OS session manager
+ // when things are hanging.
+ // Here, the current process is going to waitid() and _exit(), so there is no
+ // point in generating a crash report. The child process is the one
+ // blocking us.
+ if (signal(SIGABRT, ExitWithErrorSignalHandler) == SIG_ERR) {
+ FatalError("Failed to change signal handler");
+ }
+
+ int wait_ret =
+ HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
+
+ if (!wait_ret && reaped_child_info.si_pid == child_pid) {
+ if (reaped_child_info.si_code == CLD_EXITED) {
+ exit_code = reaped_child_info.si_status;
+ } else {
+ // Exit with code 0 if the child got signaled.
+ exit_code = 0;
+ }
+ }
+ _exit(exit_code);
+}
+
+static bool MoveToNewNamespaces() {
+ // These are the sets of flags which we'll try, in order.
+ const int kCloneExtraFlags[] = {CLONE_NEWPID | CLONE_NEWNET, CLONE_NEWPID, };
+
+ // We need to close kZygoteIdFd before the child can continue. We use this
+ // socketpair to tell the child when to continue;
+ int sync_fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+ FatalError("Failed to create a socketpair");
+ }
+
+ for (size_t i = 0; i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
+ i++) {
+ pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
+ const int clone_errno = errno;
+
+ if (pid > 0) {
+ if (!DropRoot()) {
+ FatalError("Could not drop privileges");
+ } else {
+ if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
+ FatalError("Could not close socketpair");
+ // The kZygoteIdFd needs to be closed in the parent before
+ // Zygote gets started.
+ if (close(kZygoteIdFd))
+ FatalError("close");
+ // Tell our child to continue
+ if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
+ FatalError("send");
+ if (close(sync_fds[1]))
+ FatalError("close");
+ // We want to keep a full process tree and we don't want our childs to
+ // be reparented to (the outer PID namespace) init. So we wait for it.
+ WaitForChildAndExit(pid);
+ }
+ // NOTREACHED
+ FatalError("Not reached");
+ }
+
+ if (pid == 0) {
+ if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
+ FatalError("Could not close socketpair");
+
+ // Wait for the parent to confirm it closed kZygoteIdFd before we
+ // continue
+ char should_continue;
+ if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
+ FatalError("Read on socketpair");
+ if (close(sync_fds[0]))
+ FatalError("close");
+
+ if (kCloneExtraFlags[i] & CLONE_NEWPID) {
+ setenv(kSandboxPIDNSEnvironmentVarName, "", 1 /* overwrite */);
+ } else {
+ unsetenv(kSandboxPIDNSEnvironmentVarName);
+ }
+
+ if (kCloneExtraFlags[i] & CLONE_NEWNET) {
+ setenv(kSandboxNETNSEnvironmentVarName, "", 1 /* overwrite */);
+ } else {
+ unsetenv(kSandboxNETNSEnvironmentVarName);
+ }
+
+ break;
+ }
+
+ // If EINVAL then the system doesn't support the requested flags, so
+ // continue to try a different set.
+ // On any other errno value the system *does* support these flags but
+ // something went wrong, hence we bail with an error message rather then
+ // provide less security.
+ if (errno != EINVAL) {
+ fprintf(stderr, "Failed to move to new namespace:");
+ if (kCloneExtraFlags[i] & CLONE_NEWPID) {
+ fprintf(stderr, " PID namespaces supported,");
+ }
+ if (kCloneExtraFlags[i] & CLONE_NEWNET) {
+ fprintf(stderr, " Network namespace supported,");
+ }
+ fprintf(stderr, " but failed: errno = %s\n", strerror(clone_errno));
+ return false;
+ }
+ }
+
+ // If the system doesn't support NEWPID then we carry on anyway.
+ return true;
+}
+
+static bool DropRoot() {
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
+ perror("prctl(PR_SET_DUMPABLE)");
+ return false;
+ }
+
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
+ return false;
+ }
+
+ gid_t rgid, egid, sgid;
+ if (getresgid(&rgid, &egid, &sgid)) {
+ perror("getresgid");
+ return false;
+ }
+
+ if (setresgid(rgid, rgid, rgid)) {
+ perror("setresgid");
+ return false;
+ }
+
+ uid_t ruid, euid, suid;
+ if (getresuid(&ruid, &euid, &suid)) {
+ perror("getresuid");
+ return false;
+ }
+
+ if (setresuid(ruid, ruid, ruid)) {
+ perror("setresuid");
+ return false;
+ }
+
+ return true;
+}
+
+static bool SetupChildEnvironment() {
+ unsigned i;
+
+ // ld.so may have cleared several environment variables because we are SUID.
+ // However, the child process might need them so zygote_host_linux.cc saves a
+ // copy in SANDBOX_$x. This is safe because we have dropped root by this
+ // point, so we can only exec a binary with the permissions of the user who
+ // ran us in the first place.
+
+ for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+ const char* const envvar = kSUIDUnsafeEnvironmentVariables[i];
+ char* const saved_envvar = SandboxSavedEnvironmentVariable(envvar);
+ if (!saved_envvar)
+ return false;
+
+ const char* const value = getenv(saved_envvar);
+ if (value) {
+ setenv(envvar, value, 1 /* overwrite */);
+ unsetenv(saved_envvar);
+ }
+
+ free(saved_envvar);
+ }
+
+ return true;
+}
+
+bool CheckAndExportApiVersion() {
+ // Check the environment to see if a specific API version was requested.
+ // assume version 0 if none.
+ long api_number = -1;
+ char* api_string = getenv(kSandboxEnvironmentApiRequest);
+ if (!api_string) {
+ api_number = 0;
+ } else {
+ errno = 0;
+ char* endptr = NULL;
+ api_number = strtol(api_string, &endptr, 10);
+ if (!endptr || *endptr || errno != 0)
+ return false;
+ }
+
+ // Warn only for now.
+ if (api_number != kSUIDSandboxApiNumber) {
+ fprintf(
+ stderr,
+ "The setuid sandbox provides API version %ld, "
+ "but you need %ld\n"
+ "Please read "
+ "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment."
+ "\n\n",
+ kSUIDSandboxApiNumber,
+ api_number);
+ }
+
+ // Export our version so that the sandboxed process can verify it did not
+ // use an old sandbox.
+ char version_string[64];
+ snprintf(
+ version_string, sizeof(version_string), "%ld", kSUIDSandboxApiNumber);
+ if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) {
+ perror("setenv");
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char** argv) {
+ if (argc <= 1) {
+ if (argc <= 0) {
+ return 1;
+ }
+
+ fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
+ return 1;
+ }
+
+ // Allow someone to query our API version
+ if (argc == 2 && 0 == strcmp(argv[1], kSuidSandboxGetApiSwitch)) {
+ printf("%ld\n", kSUIDSandboxApiNumber);
+ return 0;
+ }
+
+ // We cannot adjust /proc/pid/oom_adj for sandboxed renderers
+ // because those files are owned by root. So we need a helper here.
+ if (argc == 4 && (0 == strcmp(argv[1], kAdjustOOMScoreSwitch))) {
+ char* endptr = NULL;
+ long score;
+ errno = 0;
+ unsigned long pid_ul = strtoul(argv[2], &endptr, 10);
+ if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
+ return 1;
+ pid_t pid = pid_ul;
+ endptr = NULL;
+ errno = 0;
+ score = strtol(argv[3], &endptr, 10);
+ if (score == LONG_MAX || score == LONG_MIN || !endptr || *endptr ||
+ errno != 0) {
+ return 1;
+ }
+ return AdjustOOMScore(pid, score);
+ }
+
+ // Protect the core setuid sandbox functionality with an API version
+ if (!CheckAndExportApiVersion()) {
+ return 1;
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr,
+ "The setuid sandbox is not running as root. Common causes:\n"
+ " * An unprivileged process using ptrace on it, like a debugger.\n"
+ " * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n");
+ }
+
+ if (!MoveToNewNamespaces())
+ return 1;
+ if (!SpawnChrootHelper())
+ return 1;
+ if (!DropRoot())
+ return 1;
+ if (!SetupChildEnvironment())
+ return 1;
+
+ execv(argv[1], &argv[1]);
+ FatalError("execv failed");
+
+ return 1;
+}
diff --git a/sandbox/linux/tests/main.cc b/sandbox/linux/tests/main.cc
new file mode 100644
index 0000000..81a0b32
--- /dev/null
+++ b/sandbox/linux/tests/main.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/logging.h"
+#include "base/test/test_suite.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+// Check for leaks in our tests.
+void RunPostTestsChecks() {
+ if (TestUtils::CurrentProcessHasChildren()) {
+ LOG(ERROR) << "One of the tests created a child that was not waited for. "
+ << "Please, clean-up after your tests!";
+ }
+}
+
+} // namespace
+} // namespace sandbox
+
+#if defined(OS_ANDROID)
+void UnitTestAssertHandler(const std::string& str) {
+ _exit(1);
+}
+#endif
+
+int main(int argc, char* argv[]) {
+#if defined(OS_ANDROID)
+ // The use of Callbacks requires an AtExitManager.
+ base::AtExitManager exit_manager;
+ testing::InitGoogleTest(&argc, argv);
+ // Death tests rely on LOG(FATAL) triggering an exit (the default behavior is
+ // SIGABRT). The normal test launcher does this at initialization, but since
+ // we still do not use this on Android, we must install the handler ourselves.
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+#endif
+ // Always go through re-execution for death tests.
+ // This makes gtest only marginally slower for us and has the
+ // additional side effect of getting rid of gtest warnings about fork()
+ // safety.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+#if defined(OS_ANDROID)
+ int tests_result = RUN_ALL_TESTS();
+#else
+ int tests_result = base::RunUnitTestsUsingBaseTestSuite(argc, argv);
+#endif
+
+ sandbox::RunPostTestsChecks();
+ return tests_result;
+}
diff --git a/sandbox/linux/tests/sandbox_test_runner.cc b/sandbox/linux/tests/sandbox_test_runner.cc
new file mode 100644
index 0000000..b099b97
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+SandboxTestRunner::SandboxTestRunner() {
+}
+
+SandboxTestRunner::~SandboxTestRunner() {
+}
+
+bool SandboxTestRunner::ShouldCheckForLeaks() const {
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/sandbox_test_runner.h b/sandbox/linux/tests/sandbox_test_runner.h
new file mode 100644
index 0000000..3504126
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
+#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
+
+#include "base/basictypes.h"
+
+namespace sandbox {
+
+// A simple "runner" class to implement tests.
+class SandboxTestRunner {
+ public:
+ SandboxTestRunner();
+ virtual ~SandboxTestRunner();
+
+ virtual void Run() = 0;
+
+ // Override to decide whether or not to check for leaks with LSAN
+ // (if built with LSAN and LSAN is enabled).
+ virtual bool ShouldCheckForLeaks() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc
new file mode 100644
index 0000000..69e05ac
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/sandbox_test_runner_function_pointer.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace sandbox {
+
+SandboxTestRunnerFunctionPointer::SandboxTestRunnerFunctionPointer(
+ void (*function_to_run)(void))
+ : function_to_run_(function_to_run) {
+}
+
+SandboxTestRunnerFunctionPointer::~SandboxTestRunnerFunctionPointer() {
+}
+
+void SandboxTestRunnerFunctionPointer::Run() {
+ DCHECK(function_to_run_);
+ function_to_run_();
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.h b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h
new file mode 100644
index 0000000..f52db0c
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_
+#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+class SandboxTestRunnerFunctionPointer : public SandboxTestRunner {
+ public:
+ SandboxTestRunnerFunctionPointer(void (*function_to_run)(void));
+ virtual ~SandboxTestRunnerFunctionPointer() override;
+ virtual void Run() override;
+
+ private:
+ void (*function_to_run_)(void);
+ DISALLOW_COPY_AND_ASSIGN(SandboxTestRunnerFunctionPointer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER__FUNCTION_POINTER_H_
diff --git a/sandbox/linux/tests/scoped_temporary_file.cc b/sandbox/linux/tests/scoped_temporary_file.cc
new file mode 100644
index 0000000..82df9e0
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+namespace sandbox {
+
+ScopedTemporaryFile::ScopedTemporaryFile() : fd_(-1) {
+#if defined(OS_ANDROID)
+ static const char file_template[] = "/data/local/tmp/ScopedTempFileXXXXXX";
+#else
+ static const char file_template[] = "/tmp/ScopedTempFileXXXXXX";
+#endif // defined(OS_ANDROID)
+ COMPILE_ASSERT(sizeof(full_file_name_) >= sizeof(file_template),
+ full_file_name_is_large_enough);
+ memcpy(full_file_name_, file_template, sizeof(file_template));
+ fd_ = mkstemp(full_file_name_);
+ CHECK_LE(0, fd_);
+}
+
+ScopedTemporaryFile::~ScopedTemporaryFile() {
+ CHECK_EQ(0, unlink(full_file_name_));
+ CHECK_EQ(0, IGNORE_EINTR(close(fd_)));
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/scoped_temporary_file.h b/sandbox/linux/tests/scoped_temporary_file.h
new file mode 100644
index 0000000..0734130
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
+#define SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
+
+#include "base/macros.h"
+
+namespace sandbox {
+// Creates and open a temporary file on creation and closes
+// and removes it on destruction.
+// Unlike base/ helpers, this does not require JNI on Android.
+class ScopedTemporaryFile {
+ public:
+ ScopedTemporaryFile();
+ ~ScopedTemporaryFile();
+
+ int fd() const { return fd_; }
+ const char* full_file_name() const { return full_file_name_; }
+
+ private:
+ int fd_;
+ char full_file_name_[128];
+ DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
diff --git a/sandbox/linux/tests/scoped_temporary_file_unittest.cc b/sandbox/linux/tests/scoped_temporary_file_unittest.cc
new file mode 100644
index 0000000..44a2ecb
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool FullWrite(int fd, const char* buffer, size_t count) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(write(fd, buffer, count));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ }
+ return true;
+}
+
+bool FullRead(int fd, char* buffer, size_t count) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(read(fd, buffer, count));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ }
+ return true;
+}
+
+TEST(ScopedTemporaryFile, Basics) {
+ std::string temp_file_name;
+ {
+ ScopedTemporaryFile temp_file_1;
+ const char kTestString[] = "This is a test";
+ ASSERT_LE(0, temp_file_1.fd());
+
+ temp_file_name = temp_file_1.full_file_name();
+ base::ScopedFD temp_file_2(open(temp_file_1.full_file_name(), O_RDONLY));
+ ASSERT_TRUE(temp_file_2.is_valid());
+
+ ASSERT_TRUE(FullWrite(temp_file_1.fd(), kTestString, sizeof(kTestString)));
+
+ char test_string_read[sizeof(kTestString)] = {0};
+ ASSERT_TRUE(FullRead(
+ temp_file_2.get(), test_string_read, sizeof(test_string_read)));
+ ASSERT_EQ(0, memcmp(kTestString, test_string_read, sizeof(kTestString)));
+ }
+
+ errno = 0;
+ struct stat buf;
+ ASSERT_EQ(-1, stat(temp_file_name.c_str(), &buf));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.cc b/sandbox/linux/tests/test_utils.cc
new file mode 100644
index 0000000..398654b
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/test_utils.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+bool TestUtils::CurrentProcessHasChildren() {
+ siginfo_t process_info;
+ int ret = HANDLE_EINTR(
+ waitid(P_ALL, 0, &process_info, WEXITED | WNOHANG | WNOWAIT));
+ if (-1 == ret) {
+ PCHECK(ECHILD == errno);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.h b/sandbox/linux/tests/test_utils.h
new file mode 100644
index 0000000..3269847
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.h
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+#define SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+
+#include "base/basictypes.h"
+
+namespace sandbox {
+
+// This class provide small helpers to help writing tests.
+class TestUtils {
+ public:
+ static bool CurrentProcessHasChildren();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TestUtils);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_TEST_UTILS_H_
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
new file mode 100644
index 0000000..b7c3af6
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/debug/leak_annotations.h"
+#include "base/files/file_util.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace {
+std::string TestFailedMessage(const std::string& msg) {
+ return msg.empty() ? std::string() : "Actual test failure: " + msg;
+}
+
+int GetSubProcessTimeoutTimeInSeconds() {
+ // 10s ought to be enough for anybody.
+ return 10;
+}
+
+// Returns the number of threads of the current process or -1.
+int CountThreads() {
+ struct stat task_stat;
+ int task_d = stat("/proc/self/task", &task_stat);
+ // task_stat.st_nlink should be the number of tasks + 2 (accounting for
+ // "." and "..".
+ if (task_d != 0 || task_stat.st_nlink < 3)
+ return -1;
+ const int num_threads = task_stat.st_nlink - 2;
+ return num_threads;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool IsAndroid() {
+#if defined(OS_ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool IsArchitectureArm() {
+#if defined(ARCH_CPU_ARM_FAMILY)
+ return true;
+#else
+ return false;
+#endif
+}
+
+// TODO(jln): figure out why base/.../dynamic_annotations.h's
+// RunningOnValgrind() cannot link.
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+static const int kExpectedValue = 42;
+static const int kIgnoreThisTest = 43;
+static const int kExitWithAssertionFailure = 1;
+static const int kExitForTimeout = 2;
+
+#if !defined(OS_ANDROID)
+// This is due to StackDumpSignalHandler() performing _exit(1).
+// TODO(jln): get rid of the collision with kExitWithAssertionFailure.
+const int kExitAfterSIGSEGV = 1;
+#endif
+
+static void SigAlrmHandler(int) {
+ const char failure_message[] = "Timeout reached!\n";
+ // Make sure that we never block here.
+ if (!fcntl(2, F_SETFL, O_NONBLOCK)) {
+ ignore_result(write(2, failure_message, sizeof(failure_message) - 1));
+ }
+ _exit(kExitForTimeout);
+}
+
+// Set a timeout with a handler that will automatically fail the
+// test.
+static void SetProcessTimeout(int time_in_seconds) {
+ struct sigaction act = {};
+ act.sa_handler = SigAlrmHandler;
+ SANDBOX_ASSERT(sigemptyset(&act.sa_mask) == 0);
+ act.sa_flags = 0;
+
+ struct sigaction old_act;
+ SANDBOX_ASSERT(sigaction(SIGALRM, &act, &old_act) == 0);
+
+ // We don't implemenet signal chaining, so make sure that nothing else
+ // is expecting to handle SIGALRM.
+ SANDBOX_ASSERT((old_act.sa_flags & SA_SIGINFO) == 0);
+ SANDBOX_ASSERT(old_act.sa_handler == SIG_DFL);
+ sigset_t sigalrm_set;
+ SANDBOX_ASSERT(sigemptyset(&sigalrm_set) == 0);
+ SANDBOX_ASSERT(sigaddset(&sigalrm_set, SIGALRM) == 0);
+ SANDBOX_ASSERT(sigprocmask(SIG_UNBLOCK, &sigalrm_set, NULL) == 0);
+ SANDBOX_ASSERT(alarm(time_in_seconds) == 0); // There should be no previous
+ // alarm.
+}
+
+// Runs a test in a sub-process. This is necessary for most of the code
+// in the BPF sandbox, as it potentially makes global state changes and as
+// it also tends to raise fatal errors, if the code has been used in an
+// insecure manner.
+void UnitTests::RunTestInProcess(SandboxTestRunner* test_runner,
+ DeathCheck death,
+ const void* death_aux) {
+ CHECK(test_runner);
+ // We need to fork(), so we can't be multi-threaded, as threads could hold
+ // locks.
+ int num_threads = CountThreads();
+#if !defined(THREAD_SANITIZER)
+ const int kNumExpectedThreads = 1;
+#else
+ // Under TSAN, there is a special helper thread. It should be completely
+ // invisible to our testing, so we ignore it. It should be ok to fork()
+ // with this thread. It's currently buggy, but it's the best we can do until
+ // there is a way to delay the start of the thread
+ // (https://code.google.com/p/thread-sanitizer/issues/detail?id=19).
+ const int kNumExpectedThreads = 2;
+#endif
+
+ // The kernel is at liberty to wake a thread id futex before updating /proc.
+ // If another test running in the same process has stopped a thread, it may
+ // appear as still running in /proc.
+ // We poll /proc, with an exponential back-off. At most, we'll sleep around
+ // 2^iterations nanoseconds in nanosleep().
+ for (unsigned int iteration = 0; iteration < 30; iteration++) {
+ struct timespec ts = {0, 1L << iteration /* nanoseconds */};
+ PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts)));
+ num_threads = CountThreads();
+ if (kNumExpectedThreads == num_threads)
+ break;
+ }
+
+ ASSERT_EQ(kNumExpectedThreads, num_threads)
+ << "Running sandbox tests with multiple threads "
+ << "is not supported and will make the tests flaky.";
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+ // Check that our pipe is not on one of the standard file descriptor.
+ SANDBOX_ASSERT(fds[0] > 2 && fds[1] > 2);
+
+ pid_t pid;
+ ASSERT_LE(0, (pid = fork()));
+ if (!pid) {
+ // In child process
+ // Redirect stderr to our pipe. This way, we can capture all error
+ // messages, if we decide we want to do so in our tests.
+ SANDBOX_ASSERT(dup2(fds[1], 2) == 2);
+ SANDBOX_ASSERT(!close(fds[0]));
+ SANDBOX_ASSERT(!close(fds[1]));
+
+ // Don't set a timeout if running on Valgrind, since it's generally much
+ // slower.
+ if (!IsRunningOnValgrind()) {
+ SetProcessTimeout(GetSubProcessTimeoutTimeInSeconds());
+ }
+
+ // Disable core files. They are not very useful for our individual test
+ // cases.
+ struct rlimit no_core = {0};
+ setrlimit(RLIMIT_CORE, &no_core);
+
+ test_runner->Run();
+ if (test_runner->ShouldCheckForLeaks()) {
+#if defined(LEAK_SANITIZER)
+ __lsan_do_leak_check();
+#endif
+ }
+ _exit(kExpectedValue);
+ }
+
+ close(fds[1]);
+ std::vector<char> msg_buf;
+ ssize_t rc;
+
+ // Make sure read() will never block as we'll use poll() to
+ // block with a timeout instead.
+ const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK);
+ ASSERT_EQ(0, fcntl_ret);
+ struct pollfd poll_fd = {fds[0], POLLIN | POLLRDHUP, 0};
+
+ int poll_ret;
+ // We prefer the SIGALRM timeout to trigger in the child than this timeout
+ // so we double the common value here.
+ int poll_timeout = GetSubProcessTimeoutTimeInSeconds() * 2 * 1000;
+ while ((poll_ret = poll(&poll_fd, 1, poll_timeout) > 0)) {
+ const size_t kCapacity = 256;
+ const size_t len = msg_buf.size();
+ msg_buf.resize(len + kCapacity);
+ rc = HANDLE_EINTR(read(fds[0], &msg_buf[len], kCapacity));
+ msg_buf.resize(len + std::max(rc, static_cast<ssize_t>(0)));
+ if (rc <= 0)
+ break;
+ }
+ ASSERT_NE(poll_ret, -1) << "poll() failed";
+ ASSERT_NE(poll_ret, 0) << "Timeout while reading child state";
+ close(fds[0]);
+ std::string msg(msg_buf.begin(), msg_buf.end());
+
+ int status = 0;
+ int waitpid_returned = HANDLE_EINTR(waitpid(pid, &status, 0));
+ ASSERT_EQ(pid, waitpid_returned) << TestFailedMessage(msg);
+
+ // At run-time, we sometimes decide that a test shouldn't actually
+ // run (e.g. when testing sandbox features on a kernel that doesn't
+ // have sandboxing support). When that happens, don't attempt to
+ // call the "death" function, as it might be looking for a
+ // death-test condition that would never have triggered.
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != kIgnoreThisTest ||
+ !msg.empty()) {
+ // We use gtest's ASSERT_XXX() macros instead of the DeathCheck
+ // functions. This means, on failure, "return" is called. This
+ // only works correctly, if the call of the "death" callback is
+ // the very last thing in our function.
+ death(status, msg, death_aux);
+ }
+}
+
+void UnitTests::DeathSuccess(int status, const std::string& msg, const void*) {
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details;
+ bool subprocess_exited_but_printed_messages = !msg.empty();
+ EXPECT_FALSE(subprocess_exited_but_printed_messages) << details;
+}
+
+void UnitTests::DeathSuccessAllowNoise(int status,
+ const std::string& msg,
+ const void*) {
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details;
+}
+
+void UnitTests::DeathMessage(int status,
+ const std::string& msg,
+ const void* aux) {
+ std::string details(TestFailedMessage(msg));
+ const char* expected_msg = static_cast<const char*>(aux);
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << "Exit status: " << status
+ << " " << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(1, subprocess_exit_status) << details;
+
+ bool subprocess_exited_without_matching_message =
+ msg.find(expected_msg) == std::string::npos;
+ EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
+}
+
+void UnitTests::DeathSEGVMessage(int status,
+ const std::string& msg,
+ const void* aux) {
+ std::string details(TestFailedMessage(msg));
+ const char* expected_msg = static_cast<const char*>(aux);
+
+#if defined(OS_ANDROID)
+ const bool subprocess_got_sigsegv =
+ WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status));
+#else
+ const bool subprocess_got_sigsegv =
+ WIFEXITED(status) && (kExitAfterSIGSEGV == WEXITSTATUS(status));
+#endif
+
+ ASSERT_TRUE(subprocess_got_sigsegv) << "Exit status: " << status
+ << " " << details;
+
+ bool subprocess_exited_without_matching_message =
+ msg.find(expected_msg) == std::string::npos;
+ EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
+}
+
+void UnitTests::DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux) {
+ int expected_exit_code = static_cast<int>(reinterpret_cast<intptr_t>(aux));
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(expected_exit_code, subprocess_exit_status) << details;
+}
+
+void UnitTests::DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux) {
+ int expected_signo = static_cast<int>(reinterpret_cast<intptr_t>(aux));
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_by_signal = WIFSIGNALED(status);
+ ASSERT_TRUE(subprocess_terminated_by_signal) << details;
+ int subprocess_signal_number = WTERMSIG(status);
+ ASSERT_EQ(expected_signo, subprocess_signal_number) << details;
+}
+
+void UnitTests::AssertionFailure(const char* expr, const char* file, int line) {
+ fprintf(stderr, "%s:%d:%s", file, line, expr);
+ fflush(stderr);
+ _exit(kExitWithAssertionFailure);
+}
+
+void UnitTests::IgnoreThisTest() {
+ fflush(stderr);
+ _exit(kIgnoreThisTest);
+}
+
+} // namespace
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
new file mode 100644
index 0000000..f745e13
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests.h
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_UNIT_TESTS_H__
+#define SANDBOX_LINUX_TESTS_UNIT_TESTS_H__
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/sandbox_test_runner_function_pointer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Has this been compiled to run on Android?
+bool IsAndroid();
+
+bool IsArchitectureArm();
+
+// Is Valgrind currently being used?
+bool IsRunningOnValgrind();
+
+#if defined(ADDRESS_SANITIZER)
+#define DISABLE_ON_ASAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ASAN(test_name) test_name
+#endif // defined(ADDRESS_SANITIZER)
+
+#if defined(LEAK_SANITIZER)
+#define DISABLE_ON_LSAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_LSAN(test_name) test_name
+#endif
+
+#if defined(THREAD_SANITIZER)
+#define DISABLE_ON_TSAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_TSAN(test_name) test_name
+#endif // defined(THREAD_SANITIZER)
+
+#if defined(OS_ANDROID)
+#define DISABLE_ON_ANDROID(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ANDROID(test_name) test_name
+#endif
+
+// While it is perfectly OK for a complex test to provide its own DeathCheck
+// function. Most death tests have very simple requirements. These tests should
+// use one of the predefined DEATH_XXX macros as an argument to
+// SANDBOX_DEATH_TEST(). You can check for a (sub-)string in the output of the
+// test, for a particular exit code, or for a particular death signal.
+// NOTE: If you do decide to write your own DeathCheck, make sure to use
+// gtests's ASSERT_XXX() macros instead of SANDBOX_ASSERT(). See
+// unit_tests.cc for examples.
+#define DEATH_SUCCESS() sandbox::UnitTests::DeathSuccess, NULL
+#define DEATH_SUCCESS_ALLOW_NOISE() \
+ sandbox::UnitTests::DeathSuccessAllowNoise, NULL
+#define DEATH_MESSAGE(msg) \
+ sandbox::UnitTests::DeathMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_SEGV_MESSAGE(msg) \
+ sandbox::UnitTests::DeathSEGVMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_EXIT_CODE(rc) \
+ sandbox::UnitTests::DeathExitCode, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(rc))
+#define DEATH_BY_SIGNAL(s) \
+ sandbox::UnitTests::DeathBySignal, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(s))
+
+// A SANDBOX_DEATH_TEST is just like a SANDBOX_TEST (see below), but it assumes
+// that the test actually dies. The death test only passes if the death occurs
+// in the expected fashion, as specified by "death" and "death_aux". These two
+// parameters are typically set to one of the DEATH_XXX() macros.
+#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \
+ void TEST_##test_name(void); \
+ TEST(test_case_name, test_name) { \
+ SandboxTestRunnerFunctionPointer sandbox_test_runner(TEST_##test_name); \
+ sandbox::UnitTests::RunTestInProcess(&sandbox_test_runner, death); \
+ } \
+ void TEST_##test_name(void)
+
+// Define a new test case that runs inside of a GTest death test. This is
+// necessary, as most of our tests by definition make global and irreversible
+// changes to the system (i.e. they install a sandbox). GTest provides death
+// tests as a tool to isolate global changes from the rest of the tests.
+#define SANDBOX_TEST(test_case_name, test_name) \
+ SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS())
+
+// SANDBOX_TEST_ALLOW_NOISE is just like SANDBOX_TEST, except it does not
+// consider log error messages printed by the test to be test failures.
+#define SANDBOX_TEST_ALLOW_NOISE(test_case_name, test_name) \
+ SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS_ALLOW_NOISE())
+
+// Simple assertion macro that is compatible with running inside of a death
+// test. We unfortunately cannot use any of the GTest macros.
+#define SANDBOX_STR(x) #x
+#define SANDBOX_ASSERT(expr) \
+ ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \
+ SANDBOX_STR(expr), __FILE__, __LINE__))
+
+// This class allows to run unittests in their own process. The main method is
+// RunTestInProcess().
+class UnitTests {
+ public:
+ typedef void (*DeathCheck)(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // Runs a test inside a short-lived process. Do not call this function
+ // directly. It is automatically invoked by SANDBOX_TEST(). Most sandboxing
+ // functions make global irreversible changes to the execution environment
+ // and must therefore execute in their own isolated process.
+ // |test_runner| must implement the SandboxTestRunner interface and will run
+ // in a subprocess.
+ // Note: since the child process (created with fork()) will never return from
+ // RunTestInProcess(), |test_runner| is guaranteed to exist for the lifetime
+ // of the child process.
+ static void RunTestInProcess(SandboxTestRunner* test_runner,
+ DeathCheck death,
+ const void* death_aux);
+
+ // Report a useful error message and terminate the current SANDBOX_TEST().
+ // Calling this function from outside a SANDBOX_TEST() is unlikely to do
+ // anything useful.
+ static void AssertionFailure(const char* expr, const char* file, int line);
+
+ // Sometimes we determine at run-time that a test should be disabled.
+ // Call this method if we want to return from a test and completely
+ // ignore its results.
+ // You should not call this method, if the test already ran any test-relevant
+ // code. Most notably, you should not call it, you already wrote any messages
+ // to stderr.
+ static void IgnoreThisTest();
+
+ // A DeathCheck method that verifies that the test completed succcessfully.
+ // This is the default test mode for SANDBOX_TEST(). The "aux" parameter
+ // of this DeathCheck is unused (and thus unnamed)
+ static void DeathSuccess(int status, const std::string& msg, const void*);
+
+ // A DeathCheck method that verifies that the test completed succcessfully
+ // allowing for log error messages.
+ static void DeathSuccessAllowNoise(int status,
+ const std::string& msg,
+ const void*);
+
+ // A DeathCheck method that verifies that the test completed with error
+ // code "1" and printed a message containing a particular substring. The
+ // "aux" pointer should point to a C-string containing the expected error
+ // message. This method is useful for checking assertion failures such as
+ // in SANDBOX_ASSERT() and/or SANDBOX_DIE().
+ static void DeathMessage(int status, const std::string& msg, const void* aux);
+
+ // Like DeathMessage() but the process must be terminated with a segmentation
+ // fault.
+ // Implementation detail: On Linux (but not on Android), this does check for
+ // the return value of our default signal handler rather than for the actual
+ // reception of a SIGSEGV.
+ // TODO(jln): make this more robust.
+ static void DeathSEGVMessage(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // A DeathCheck method that verifies that the test completed with a
+ // particular exit code. If the test output any messages to stderr, they are
+ // silently ignored. The expected exit code should be passed in by
+ // casting the its "int" value to a "void *", which is then used for "aux".
+ static void DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // A DeathCheck method that verifies that the test was terminated by a
+ // particular signal. If the test output any messages to stderr, they are
+ // silently ignore. The expected signal number should be passed in by
+ // casting the its "int" value to a "void *", which is then used for "aux".
+ static void DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UnitTests);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_TESTS_UNIT_TESTS_H__
diff --git a/sandbox/linux/tests/unit_tests_unittest.cc b/sandbox/linux/tests/unit_tests_unittest.cc
new file mode 100644
index 0000000..57799b1
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// Let's not use any of the "magic" values used internally in unit_tests.cc,
+// such as kExpectedValue.
+const int kExpectedExitCode = 100;
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathExitCode,
+ DEATH_EXIT_CODE(kExpectedExitCode)) {
+ _exit(kExpectedExitCode);
+}
+
+const int kExpectedSignalNumber = SIGKILL;
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathBySignal,
+ DEATH_BY_SIGNAL(kExpectedSignalNumber)) {
+ raise(kExpectedSignalNumber);
+}
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathWithMessage,
+ DEATH_MESSAGE("Hello")) {
+ LOG(ERROR) << "Hello";
+ _exit(1);
+}
+
+SANDBOX_DEATH_TEST(UnitTests,
+ SEGVDeathWithMessage,
+ DEATH_SEGV_MESSAGE("Hello")) {
+ LOG(ERROR) << "Hello";
+ while (1) {
+ volatile char* addr = reinterpret_cast<volatile char*>(NULL);
+ *addr = '\0';
+ }
+
+ _exit(2);
+}
+
+SANDBOX_TEST_ALLOW_NOISE(UnitTests, NoisyTest) {
+ LOG(ERROR) << "The cow says moo!";
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/sandbox_export.h b/sandbox/sandbox_export.h
new file mode 100644
index 0000000..40a4036
--- /dev/null
+++ b/sandbox/sandbox_export.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SANDBOX_EXPORT_H_
+#define SANDBOX_SANDBOX_EXPORT_H_
+
+#if defined(WIN32)
+#error "sandbox_export.h does not support WIN32."
+#endif
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(SANDBOX_IMPLEMENTATION)
+#define SANDBOX_EXPORT __attribute__((visibility("default")))
+#define SANDBOX_EXPORT_PRIVATE __attribute__((visibility("default")))
+#else
+#define SANDBOX_EXPORT
+#define SANDBOX_EXPORT_PRIVATE
+#endif // defined(SANDBOX_IMPLEMENTATION)
+
+#else // defined(COMPONENT_BUILD)
+
+#define SANDBOX_EXPORT
+#define SANDBOX_EXPORT_PRIVATE
+
+#endif // defined(COMPONENT_BUILD)
+
+#endif // SANDBOX_SANDBOX_EXPORT_H_
diff --git a/skia/skia.gyp b/skia/skia.gyp
index c4129db..2d2668a 100644
--- a/skia/skia.gyp
+++ b/skia/skia.gyp
@@ -17,6 +17,10 @@
'skia_library.gypi',
'skia_common.gypi',
'../build/android/increase_size_for_speed.gypi',
+ # Disable LTO due to compiler error
+ # in mems_in_disjoint_alias_sets_p, at alias.c:393
+ # crbug.com/422255
+ '../build/android/disable_lto.gypi',
],
},
],
diff --git a/skia/skia_library_opts.gyp b/skia/skia_library_opts.gyp
index 5413721..6673105 100644
--- a/skia/skia_library_opts.gyp
+++ b/skia/skia_library_opts.gyp
@@ -30,6 +30,10 @@
'includes': [
'skia_common.gypi',
'../build/android/increase_size_for_speed.gypi',
+ # Disable LTO due to compiler error
+ # in mems_in_disjoint_alias_sets_p, at alias.c:393
+ # crbug.com/422255
+ '../build/android/disable_lto.gypi',
],
'include_dirs': [
'../third_party/skia/include/core',
@@ -298,6 +302,9 @@
'includes': [
'skia_common.gypi',
'../build/android/increase_size_for_speed.gypi',
+ # Disable LTO due to Neon issues
+ # crbug.com/408997
+ '../build/android/disable_lto.gypi',
],
'include_dirs': [
'../third_party/skia/include/core',
diff --git a/testing/buildbot/chromium_trybot.json b/testing/buildbot/chromium_trybot.json
index e2fe64d..534767f 100644
--- a/testing/buildbot/chromium_trybot.json
+++ b/testing/buildbot/chromium_trybot.json
@@ -189,10 +189,6 @@
"platforms": ["linux", "win"]
},
{
- "test": "mojo_view_manager_unittests",
- "platforms": ["linux"]
- },
- {
"test": "views_unittests",
"platforms": ["linux", "win"]
},
diff --git a/testing/buildbot/trybot_analyze_config.json b/testing/buildbot/trybot_analyze_config.json
index d7f445f..7921d66 100644
--- a/testing/buildbot/trybot_analyze_config.json
+++ b/testing/buildbot/trybot_analyze_config.json
@@ -3,6 +3,7 @@
"exclusions": [
".*isolate",
"build/.*gyp[i]?",
+ "build/android/.*py",
"build/android/pylib/.*",
"build/compiler_version.py",
"build/get_landmines.py",
diff --git a/testing/scripts/checkdeps.py b/testing/scripts/checkdeps.py
index 83abfdf..d6140da 100755
--- a/testing/scripts/checkdeps.py
+++ b/testing/scripts/checkdeps.py
@@ -3,41 +3,18 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import argparse
-import contextlib
import json
import os
-import subprocess
import sys
-import tempfile
-SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
-SRC_DIR = os.path.abspath(
- os.path.join(SCRIPT_DIR, os.path.pardir, os.path.pardir))
+import common
-def run_command(argv):
- print 'Running %r' % argv
- rc = subprocess.call(argv)
- print 'Command %r returned exit code %d' % (argv, rc)
- return rc
-
-
-@contextlib.contextmanager
-def temporary_file():
- fd, path = tempfile.mkstemp()
- os.close(fd)
- try:
- yield path
- finally:
- os.remove(path)
-
-
-def mode_run(args):
- with temporary_file() as tempfile_path:
- rc = run_command([
- os.path.join(SRC_DIR, 'buildtools', 'checkdeps', 'checkdeps.py'),
+def main_run(args):
+ with common.temporary_file() as tempfile_path:
+ rc = common.run_command([
+ os.path.join(common.SRC_DIR, 'buildtools', 'checkdeps', 'checkdeps.py'),
'--json', tempfile_path
])
@@ -49,27 +26,21 @@
for violation in result['violations']:
result_set.add((result['dependee_path'], violation['include_path']))
- with open(args.output, 'w') as f:
- json.dump({
- 'valid': True,
- 'failures': ['%s: %s' % (r[0], r[1]) for r in result_set],
- }, f)
+ json.dump({
+ 'valid': True,
+ 'failures': ['%s: %s' % (r[0], r[1]) for r in result_set],
+ }, args.output)
return rc
-def main(argv):
- parser = argparse.ArgumentParser()
-
- subparsers = parser.add_subparsers()
-
- run_parser = subparsers.add_parser('run')
- run_parser.add_argument('--output', required=True)
- run_parser.set_defaults(func=mode_run)
-
- args = parser.parse_args(argv)
- return args.func(args)
+def main_compile_targets(args):
+ json.dump([], args.output)
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ funcs = {
+ 'run': main_run,
+ 'compile_targets': main_compile_targets,
+ }
+ sys.exit(common.run_script(sys.argv[1:], funcs))
diff --git a/testing/scripts/common.py b/testing/scripts/common.py
new file mode 100644
index 0000000..92ad688
--- /dev/null
+++ b/testing/scripts/common.py
@@ -0,0 +1,117 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import contextlib
+import json
+import os
+import subprocess
+import tempfile
+
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+SRC_DIR = os.path.abspath(
+ os.path.join(SCRIPT_DIR, os.path.pardir, os.path.pardir))
+
+
+# run-webkit-tests returns the number of failures as the return
+# code, but caps the return code at 101 to avoid overflow or colliding
+# with reserved values from the shell.
+MAX_FAILURES_EXIT_STATUS = 101
+
+
+def run_script(argv, funcs):
+ def parse_json(path):
+ with open(path) as f:
+ return json.load(f)
+ parser = argparse.ArgumentParser()
+ # TODO(phajdan.jr): Make build-config-fs required after passing it in recipe.
+ parser.add_argument('--build-config-fs')
+ parser.add_argument('--paths', type=parse_json, default={})
+ parser.add_argument('--properties', type=parse_json, default={})
+
+ subparsers = parser.add_subparsers()
+
+ run_parser = subparsers.add_parser('run')
+ run_parser.add_argument(
+ '--output', type=argparse.FileType('w'), required=True)
+ run_parser.add_argument('--filter-file', type=argparse.FileType('r'))
+ run_parser.set_defaults(func=funcs['run'])
+
+ run_parser = subparsers.add_parser('compile_targets')
+ run_parser.add_argument(
+ '--output', type=argparse.FileType('w'), required=True)
+ run_parser.set_defaults(func=funcs['compile_targets'])
+
+ args = parser.parse_args(argv)
+ return args.func(args)
+
+
+def run_command(argv):
+ print 'Running %r' % argv
+ rc = subprocess.call(argv)
+ print 'Command %r returned exit code %d' % (argv, rc)
+ return rc
+
+
+@contextlib.contextmanager
+def temporary_file():
+ fd, path = tempfile.mkstemp()
+ os.close(fd)
+ try:
+ yield path
+ finally:
+ os.remove(path)
+
+
+def parse_common_test_results(json_results):
+ def convert_trie_to_flat_paths(trie, prefix=None):
+ # Also see webkitpy.layout_tests.layout_package.json_results_generator
+ result = {}
+ for name, data in trie.iteritems():
+ if prefix:
+ name = prefix + '/' + name
+ if len(data) and not 'actual' in data and not 'expected' in data:
+ result.update(convert_trie_to_flat_paths(data, name))
+ else:
+ result[name] = data
+ return result
+
+ results = {
+ 'passes': {},
+ 'unexpected_passes': {},
+ 'failures': {},
+ 'unexpected_failures': {},
+ 'flakes': {},
+ 'unexpected_flakes': {},
+ }
+
+ # TODO(dpranke): crbug.com/357866 - we should simplify the handling of
+ # both the return code and parsing the actual results, below.
+
+ passing_statuses = ('PASS', 'SLOW', 'NEEDSREBASELINE',
+ 'NEEDSMANUALREBASELINE')
+
+ for test, result in convert_trie_to_flat_paths(
+ json_results['tests']).iteritems():
+ key = 'unexpected_' if result.get('is_unexpected') else ''
+ data = result['actual']
+ actual_results = data.split()
+ last_result = actual_results[-1]
+ expected_results = result['expected'].split()
+
+ if (len(actual_results) > 1 and
+ (last_result in expected_results or last_result in passing_statuses)):
+ key += 'flakes'
+ elif last_result in passing_statuses:
+ key += 'passes'
+ # TODO(dpranke): crbug.com/357867 ... Why are we assigning result
+ # instead of actual_result here. Do we even need these things to be
+ # hashes, or just lists?
+ data = result
+ else:
+ key += 'failures'
+ results[key][test] = data
+
+ return results
diff --git a/testing/scripts/get_compile_targets.py b/testing/scripts/get_compile_targets.py
new file mode 100755
index 0000000..16cff95
--- /dev/null
+++ b/testing/scripts/get_compile_targets.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import json
+import os
+import sys
+
+import common
+
+
+def main(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--output', required=True)
+ parser.add_argument('args', nargs=argparse.REMAINDER)
+
+ args = parser.parse_args(argv)
+
+ passthrough_args = args.args
+ if passthrough_args[0] == '--':
+ passthrough_args = passthrough_args[1:]
+
+ results = {}
+
+ for filename in os.listdir(common.SCRIPT_DIR):
+ if not filename.endswith('.py'):
+ continue
+ if filename in ('common.py', 'get_compile_targets.py'):
+ continue
+
+ with common.temporary_file() as tempfile_path:
+ rc = common.run_command(
+ [sys.executable, os.path.join(common.SCRIPT_DIR, filename)] +
+ passthrough_args +
+ [
+ 'compile_targets',
+ '--output', tempfile_path
+ ]
+ )
+ if rc != 0:
+ return rc
+
+ with open(tempfile_path) as f:
+ results[filename] = json.load(f)
+
+ with open(args.output, 'w') as f:
+ json.dump(results, f)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/testing/scripts/telemetry_unittests.py b/testing/scripts/telemetry_unittests.py
new file mode 100755
index 0000000..129304e
--- /dev/null
+++ b/testing/scripts/telemetry_unittests.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import sys
+
+
+import common
+
+
+def main_run(args):
+ filter_tests = []
+ if args.filter_file:
+ filter_tests = json.load(args.filter_file)
+
+ with common.temporary_file() as tempfile_path:
+ rc = common.run_command([
+ sys.executable,
+ os.path.join(args.paths['build'], 'scripts', 'tools', 'runit.py'),
+ '--show-path',
+ os.path.join(args.paths['build'], 'scripts', 'slave', 'runtest.py'),
+ '--target', args.build_config_fs,
+ '--xvfb',
+ '--annotate', 'gtest',
+ '--test-type', 'telemetry_unittests',
+ '--builder-name', args.properties['buildername'],
+ '--slave-name', args.properties['slavename'],
+ '--build-number', str(args.properties['buildnumber']),
+ '--run-python-script',
+ os.path.join(common.SRC_DIR, 'tools', 'telemetry', 'run_tests'),
+ '--browser', args.build_config_fs.lower(),
+ '--retry-limit', '3',
+ '--write-full-results-to', tempfile_path,
+ ] + filter_tests)
+
+ with open(tempfile_path) as f:
+ results = json.load(f)
+
+ parsed_results = common.parse_common_test_results(results)
+ failures = parsed_results['unexpected_failures']
+
+ json.dump({
+ 'valid': bool(rc <= common.MAX_FAILURES_EXIT_STATUS and
+ ((rc == 0) or failures)),
+ 'failures': failures.keys(),
+ }, args.output)
+
+ return rc
+
+
+def main_compile_targets(args):
+ json.dump(['chrome'], args.output)
+
+
+if __name__ == '__main__':
+ funcs = {
+ 'run': main_run,
+ 'compile_targets': main_compile_targets,
+ }
+ sys.exit(common.run_script(sys.argv[1:], funcs))
diff --git a/third_party/checkstyle/LICENSE b/third_party/checkstyle/LICENSE
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/third_party/checkstyle/LICENSE
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/third_party/checkstyle/LICENSE.apache20 b/third_party/checkstyle/LICENSE.apache20
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/checkstyle/LICENSE.apache20
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/third_party/checkstyle/OWNERS b/third_party/checkstyle/OWNERS
new file mode 100644
index 0000000..577bf98
--- /dev/null
+++ b/third_party/checkstyle/OWNERS
@@ -0,0 +1,2 @@
+aurimas@chromium.org
+newt@chromium.org
diff --git a/third_party/checkstyle/README.chromium b/third_party/checkstyle/README.chromium
new file mode 100644
index 0000000..53a267c
--- /dev/null
+++ b/third_party/checkstyle/README.chromium
@@ -0,0 +1,26 @@
+Name: Checkstyle is a development tool to help programmers write Java code that
+ adheres to a coding standard.
+Short Name: checkstyle
+URL: http://checkstyle.sourceforge.net/
+Version: 5.9
+License: LGPL 2.1
+License File: LICENSE
+Security Critical: no
+
+Description:
+Checkstyle is used to validate Java code style on Chromium PRESUBMIT step.
+
+Local Modifications:
+- Removed contrib/ and site/ directories that contained examples of how to write
+ new checkstyle modules.
+- Removed xml files used for Eclipse setup when building new checkstyle modules.
+ - checkstyle_checks.xml
+ - import-control.xml
+ - supressions.xml
+- Removed jars already included in checkstyle-5.9-all.jar
+ - antlr-2.7.7.jar
+ - checkstyle-5.9.jar
+ - commons-beanutils-core-1.8.3.jar
+ - commons-cli-1.2.jar
+ - commons-logging-1.1.1.jar
+ - guava-jdk5-14.0.1.jar
diff --git a/third_party/checkstyle/RIGHTS.antlr b/third_party/checkstyle/RIGHTS.antlr
new file mode 100644
index 0000000..0fc5570
--- /dev/null
+++ b/third_party/checkstyle/RIGHTS.antlr
@@ -0,0 +1,32 @@
+
+SOFTWARE RIGHTS
+$Id: RIGHTS.antlr,v 1.1 2001-06-22 13:11:01 oburn Exp $
+
+ANTLR 1989-2000 Developed by jGuru.com (MageLang Institute),
+http://www.ANTLR.org and http://www.jGuru.com
+
+We reserve no legal rights to the ANTLR--it is fully in the
+public domain. An individual or company may do whatever
+they wish with source code distributed with ANTLR or the
+code generated by ANTLR, including the incorporation of
+ANTLR, or its output, into commerical software.
+
+We encourage users to develop software with ANTLR. However,
+we do ask that credit is given to us for developing
+ANTLR. By "credit", we mean that if you use ANTLR or
+incorporate any source code into one of your programs
+(commercial product, research project, or otherwise) that
+you acknowledge this fact somewhere in the documentation,
+research report, etc... If you like ANTLR and have
+developed a nice tool with the output, please mention that
+you developed it using ANTLR. In addition, we ask that the
+headers remain intact in our source code. As long as these
+guidelines are kept, we expect to continue enhancing this
+system and expect to make other tools available as they are
+completed.
+
+The primary ANTLR guy:
+
+Terence Parr
+http://www.jGuru.com
+parrt@jguru.com
diff --git a/third_party/checkstyle/checkstyle-5.9-all.jar b/third_party/checkstyle/checkstyle-5.9-all.jar
new file mode 100644
index 0000000..a820e2e
--- /dev/null
+++ b/third_party/checkstyle/checkstyle-5.9-all.jar
Binary files differ
diff --git a/third_party/checkstyle/java.header b/third_party/checkstyle/java.header
new file mode 100644
index 0000000..0efd4ce
--- /dev/null
+++ b/third_party/checkstyle/java.header
@@ -0,0 +1,18 @@
+////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code for adherence to a set of rules.
+// Copyright (C) 2001-2014 Oliver Burn
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+////////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/checkstyle/sun_checks.xml b/third_party/checkstyle/sun_checks.xml
new file mode 100644
index 0000000..709f3ec
--- /dev/null
+++ b/third_party/checkstyle/sun_checks.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+
+ Checkstyle configuration that checks the sun coding conventions from:
+
+ - the Java Language Specification at
+ http://java.sun.com/docs/books/jls/second_edition/html/index.html
+
+ - the Sun Code Conventions at http://java.sun.com/docs/codeconv/
+
+ - the Javadoc guidelines at
+ http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
+
+ - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
+
+ - some best practices
+
+ Checkstyle is very configurable. Be sure to read the documentation at
+ http://checkstyle.sf.net (or in your downloaded distribution).
+
+ Most Checks are configurable, be sure to consult the documentation.
+
+ To completely disable a check, just comment it out or delete it from the file.
+
+ Finally, it is worth reading the documentation.
+
+-->
+
+<module name="Checker">
+ <!--
+ If you set the basedir property below, then all reported file
+ names will be relative to the specified directory. See
+ http://checkstyle.sourceforge.net/5.x/config.html#Checker
+
+ <property name="basedir" value="${basedir}"/>
+ -->
+
+ <!-- Checks that a package-info.java file exists for each package. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
+ <module name="JavadocPackage"/>
+
+ <!-- Checks whether files end with a new line. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+ <module name="NewlineAtEndOfFile"/>
+
+ <!-- Checks that property files contain the same keys. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+ <module name="Translation"/>
+
+ <!-- Checks for Size Violations. -->
+ <!-- See http://checkstyle.sf.net/config_sizes.html -->
+ <module name="FileLength"/>
+
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="FileTabCharacter"/>
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html -->
+ <module name="RegexpSingleline">
+ <property name="format" value="\s+$"/>
+ <property name="minimum" value="0"/>
+ <property name="maximum" value="0"/>
+ <property name="message" value="Line has trailing spaces."/>
+ </module>
+
+ <!-- Checks for Headers -->
+ <!-- See http://checkstyle.sf.net/config_header.html -->
+ <!-- <module name="Header"> -->
+ <!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
+ <!-- <property name="fileExtensions" value="java"/> -->
+ <!-- </module> -->
+
+ <module name="TreeWalker">
+
+ <!-- Checks for Javadoc comments. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+ <module name="JavadocMethod"/>
+ <module name="JavadocType"/>
+ <module name="JavadocVariable"/>
+ <module name="JavadocStyle"/>
+
+
+ <!-- Checks for Naming Conventions. -->
+ <!-- See http://checkstyle.sf.net/config_naming.html -->
+ <module name="ConstantName"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="MethodName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+
+
+ <!-- Checks for imports -->
+ <!-- See http://checkstyle.sf.net/config_import.html -->
+ <module name="AvoidStarImport"/>
+ <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+ <module name="RedundantImport"/>
+ <module name="UnusedImports"/>
+
+
+ <!-- Checks for Size Violations. -->
+ <!-- See http://checkstyle.sf.net/config_sizes.html -->
+ <module name="LineLength"/>
+ <module name="MethodLength"/>
+ <module name="ParameterNumber"/>
+
+
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="EmptyForIteratorPad"/>
+ <module name="GenericWhitespace"/>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="OperatorWrap"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+
+
+ <!-- Modifier Checks -->
+ <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+ <module name="ModifierOrder"/>
+ <module name="RedundantModifier"/>
+
+
+ <!-- Checks for blocks. You know, those {}'s -->
+ <!-- See http://checkstyle.sf.net/config_blocks.html -->
+ <module name="AvoidNestedBlocks"/>
+ <module name="EmptyBlock"/>
+ <module name="LeftCurly"/>
+ <module name="NeedBraces"/>
+ <module name="RightCurly"/>
+
+
+ <!-- Checks for common coding problems -->
+ <!-- See http://checkstyle.sf.net/config_coding.html -->
+ <module name="AvoidInlineConditionals"/>
+ <module name="EmptyStatement"/>
+ <module name="EqualsHashCode"/>
+ <module name="HiddenField"/>
+ <module name="IllegalInstantiation"/>
+ <module name="InnerAssignment"/>
+ <module name="MagicNumber"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="RedundantThrows"/>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+
+ <!-- Checks for class design -->
+ <!-- See http://checkstyle.sf.net/config_design.html -->
+ <module name="DesignForExtension"/>
+ <module name="FinalClass"/>
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InterfaceIsType"/>
+ <module name="VisibilityModifier"/>
+
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html -->
+ <module name="ArrayTypeStyle"/>
+ <module name="FinalParameters"/>
+ <module name="TodoComment"/>
+ <module name="UpperEll"/>
+
+ </module>
+
+</module>
diff --git a/third_party/khronos/GLES2/gl2.h b/third_party/khronos/GLES2/gl2.h
index dc749b0..15602c6 100644
--- a/third_party/khronos/GLES2/gl2.h
+++ b/third_party/khronos/GLES2/gl2.h
@@ -6,7 +6,7 @@
#endif
/*
-** Copyright (c) 2013 The Khronos Group Inc.
+** Copyright (c) 2013-2014 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -33,13 +33,13 @@
** used to make the header, and the header can be found at
** http://www.opengl.org/registry/
**
-** Khronos $Revision: 24263 $ on $Date: 2013-12-02 03:42:02 -0800 (Mon, 02 Dec 2013) $
+** Khronos $Revision: 28366 $ on $Date: 2014-10-20 11:29:02 +0200 (Mon, 20 Oct 2014) $
*/
#include <GLES2/gl2chromium.h>
#include <GLES2/gl2platform.h>
-/* Generated on date 20131202 */
+/* Generated on date 20141020 */
/* Generated C header for:
* API: gles2
diff --git a/third_party/khronos/GLES2/gl2ext.h b/third_party/khronos/GLES2/gl2ext.h
index 5891c74..a3efa82 100644
--- a/third_party/khronos/GLES2/gl2ext.h
+++ b/third_party/khronos/GLES2/gl2ext.h
@@ -6,7 +6,7 @@
#endif
/*
-** Copyright (c) 2013 The Khronos Group Inc.
+** Copyright (c) 2013-2014 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -33,14 +33,14 @@
** used to make the header, and the header can be found at
** http://www.opengl.org/registry/
**
-** Khronos $Revision: 24263 $ on $Date: 2013-12-02 03:42:02 -0800 (Mon, 02 Dec 2013) $
+** Khronos $Revision: 28366 $ on $Date: 2014-10-20 11:29:02 +0200 (Mon, 20 Oct 2014) $
*/
#ifndef GL_APIENTRYP
#define GL_APIENTRYP GL_APIENTRY*
#endif
-/* Generated on date 20131202 */
+/* Generated on date 20141020 */
/* Generated C header for:
* API: gles2
@@ -52,6 +52,40 @@
* Extensions removed: _nomatch_^
*/
+#ifndef GL_KHR_blend_equation_advanced
+#define GL_KHR_blend_equation_advanced 1
+#define GL_MULTIPLY_KHR 0x9294
+#define GL_SCREEN_KHR 0x9295
+#define GL_OVERLAY_KHR 0x9296
+#define GL_DARKEN_KHR 0x9297
+#define GL_LIGHTEN_KHR 0x9298
+#define GL_COLORDODGE_KHR 0x9299
+#define GL_COLORBURN_KHR 0x929A
+#define GL_HARDLIGHT_KHR 0x929B
+#define GL_SOFTLIGHT_KHR 0x929C
+#define GL_DIFFERENCE_KHR 0x929E
+#define GL_EXCLUSION_KHR 0x92A0
+#define GL_HSL_HUE_KHR 0x92AD
+#define GL_HSL_SATURATION_KHR 0x92AE
+#define GL_HSL_COLOR_KHR 0x92AF
+#define GL_HSL_LUMINOSITY_KHR 0x92B0
+typedef void (GL_APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glBlendBarrierKHR (void);
+#endif
+#endif /* GL_KHR_blend_equation_advanced */
+
+#ifndef GL_KHR_blend_equation_advanced_coherent
+#define GL_KHR_blend_equation_advanced_coherent 1
+#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285
+#endif /* GL_KHR_blend_equation_advanced_coherent */
+
+#ifndef GL_KHR_context_flush_control
+#define GL_KHR_context_flush_control 1
+#define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB
+#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x82FC
+#endif /* GL_KHR_context_flush_control */
+
#ifndef GL_KHR_debug
#define GL_KHR_debug 1
typedef void (GL_APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
@@ -121,6 +155,34 @@
#endif
#endif /* GL_KHR_debug */
+#ifndef GL_KHR_robust_buffer_access_behavior
+#define GL_KHR_robust_buffer_access_behavior 1
+#endif /* GL_KHR_robust_buffer_access_behavior */
+
+#ifndef GL_KHR_robustness
+#define GL_KHR_robustness 1
+#define GL_CONTEXT_ROBUST_ACCESS_KHR 0x90F3
+#define GL_LOSE_CONTEXT_ON_RESET_KHR 0x8252
+#define GL_GUILTY_CONTEXT_RESET_KHR 0x8253
+#define GL_INNOCENT_CONTEXT_RESET_KHR 0x8254
+#define GL_UNKNOWN_CONTEXT_RESET_KHR 0x8255
+#define GL_RESET_NOTIFICATION_STRATEGY_KHR 0x8256
+#define GL_NO_RESET_NOTIFICATION_KHR 0x8261
+#define GL_CONTEXT_LOST_KHR 0x0507
+typedef GLenum (GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC) (void);
+typedef void (GL_APIENTRYP PFNGLREADNPIXELSKHRPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);
+typedef void (GL_APIENTRYP PFNGLGETNUNIFORMFVKHRPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params);
+typedef void (GL_APIENTRYP PFNGLGETNUNIFORMIVKHRPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params);
+typedef void (GL_APIENTRYP PFNGLGETNUNIFORMUIVKHRPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL GLenum GL_APIENTRY glGetGraphicsResetStatusKHR (void);
+GL_APICALL void GL_APIENTRY glReadnPixelsKHR (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data);
+GL_APICALL void GL_APIENTRY glGetnUniformfvKHR (GLuint program, GLint location, GLsizei bufSize, GLfloat *params);
+GL_APICALL void GL_APIENTRY glGetnUniformivKHR (GLuint program, GLint location, GLsizei bufSize, GLint *params);
+GL_APICALL void GL_APIENTRY glGetnUniformuivKHR (GLuint program, GLint location, GLsizei bufSize, GLuint *params);
+#endif
+#endif /* GL_KHR_robustness */
+
#ifndef GL_KHR_texture_compression_astc_hdr
#define GL_KHR_texture_compression_astc_hdr 1
#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
@@ -176,6 +238,10 @@
#define GL_SAMPLER_EXTERNAL_OES 0x8D66
#endif /* GL_OES_EGL_image_external */
+#ifndef GL_OES_compressed_ETC1_RGB8_sub_texture
+#define GL_OES_compressed_ETC1_RGB8_sub_texture 1
+#endif /* GL_OES_compressed_ETC1_RGB8_sub_texture */
+
#ifndef GL_OES_compressed_ETC1_RGB8_texture
#define GL_OES_compressed_ETC1_RGB8_texture 1
#define GL_ETC1_RGB8_OES 0x8D64
@@ -277,6 +343,31 @@
#define GL_OES_rgb8_rgba8 1
#endif /* GL_OES_rgb8_rgba8 */
+#ifndef GL_OES_sample_shading
+#define GL_OES_sample_shading 1
+#define GL_SAMPLE_SHADING_OES 0x8C36
+#define GL_MIN_SAMPLE_SHADING_VALUE_OES 0x8C37
+typedef void (GL_APIENTRYP PFNGLMINSAMPLESHADINGOESPROC) (GLfloat value);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glMinSampleShadingOES (GLfloat value);
+#endif
+#endif /* GL_OES_sample_shading */
+
+#ifndef GL_OES_sample_variables
+#define GL_OES_sample_variables 1
+#endif /* GL_OES_sample_variables */
+
+#ifndef GL_OES_shader_image_atomic
+#define GL_OES_shader_image_atomic 1
+#endif /* GL_OES_shader_image_atomic */
+
+#ifndef GL_OES_shader_multisample_interpolation
+#define GL_OES_shader_multisample_interpolation 1
+#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES 0x8E5B
+#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES 0x8E5C
+#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES 0x8E5D
+#endif /* GL_OES_shader_multisample_interpolation */
+
#ifndef GL_OES_standard_derivatives
#define GL_OES_standard_derivatives 1
#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B
@@ -372,6 +463,25 @@
#define GL_OES_texture_npot 1
#endif /* GL_OES_texture_npot */
+#ifndef GL_OES_texture_stencil8
+#define GL_OES_texture_stencil8 1
+#define GL_STENCIL_INDEX_OES 0x1901
+#define GL_STENCIL_INDEX8_OES 0x8D48
+#endif /* GL_OES_texture_stencil8 */
+
+#ifndef GL_OES_texture_storage_multisample_2d_array
+#define GL_OES_texture_storage_multisample_2d_array 1
+#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES 0x9102
+#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES 0x9105
+#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910B
+#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910C
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES 0x910D
+typedef void (GL_APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEOESPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glTexStorage3DMultisampleOES (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);
+#endif
+#endif /* GL_OES_texture_storage_multisample_2d_array */
+
#ifndef GL_OES_vertex_array_object
#define GL_OES_vertex_array_object 1
#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5
@@ -450,6 +560,10 @@
#define GL_Z400_BINARY_AMD 0x8740
#endif /* GL_AMD_program_binary_Z400 */
+#ifndef GL_ANDROID_extension_pack_es31a
+#define GL_ANDROID_extension_pack_es31a 1
+#endif /* GL_ANDROID_extension_pack_es31a */
+
#ifndef GL_ANGLE_depth_texture
#define GL_ANGLE_depth_texture 1
#endif /* GL_ANGLE_depth_texture */
@@ -525,6 +639,23 @@
#endif
#endif /* GL_ANGLE_translated_shader_source */
+#ifndef GL_APPLE_clip_distance
+#define GL_APPLE_clip_distance 1
+#define GL_MAX_CLIP_DISTANCES_APPLE 0x0D32
+#define GL_CLIP_DISTANCE0_APPLE 0x3000
+#define GL_CLIP_DISTANCE1_APPLE 0x3001
+#define GL_CLIP_DISTANCE2_APPLE 0x3002
+#define GL_CLIP_DISTANCE3_APPLE 0x3003
+#define GL_CLIP_DISTANCE4_APPLE 0x3004
+#define GL_CLIP_DISTANCE5_APPLE 0x3005
+#define GL_CLIP_DISTANCE6_APPLE 0x3006
+#define GL_CLIP_DISTANCE7_APPLE 0x3007
+#endif /* GL_APPLE_clip_distance */
+
+#ifndef GL_APPLE_color_buffer_packed_float
+#define GL_APPLE_color_buffer_packed_float 1
+#endif /* GL_APPLE_color_buffer_packed_float */
+
#ifndef GL_APPLE_copy_texture_levels
#define GL_APPLE_copy_texture_levels 1
typedef void (GL_APIENTRYP PFNGLCOPYTEXTURELEVELSAPPLEPROC) (GLuint destinationTexture, GLuint sourceTexture, GLint sourceBaseLevel, GLsizei sourceLevelCount);
@@ -605,6 +736,14 @@
#define GL_TEXTURE_MAX_LEVEL_APPLE 0x813D
#endif /* GL_APPLE_texture_max_level */
+#ifndef GL_APPLE_texture_packed_float
+#define GL_APPLE_texture_packed_float 1
+#define GL_UNSIGNED_INT_10F_11F_11F_REV_APPLE 0x8C3B
+#define GL_UNSIGNED_INT_5_9_9_9_REV_APPLE 0x8C3E
+#define GL_R11F_G11F_B10F_APPLE 0x8C3A
+#define GL_RGB9_E5_APPLE 0x8C3D
+#endif /* GL_APPLE_texture_packed_float */
+
#ifndef GL_ARM_mali_program_binary
#define GL_ARM_mali_program_binary 1
#define GL_MALI_PROGRAM_BINARY_ARM 0x8F61
@@ -619,6 +758,23 @@
#define GL_ARM_rgba8 1
#endif /* GL_ARM_rgba8 */
+#ifndef GL_ARM_shader_framebuffer_fetch
+#define GL_ARM_shader_framebuffer_fetch 1
+#define GL_FETCH_PER_SAMPLE_ARM 0x8F65
+#define GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM 0x8F66
+#endif /* GL_ARM_shader_framebuffer_fetch */
+
+#ifndef GL_ARM_shader_framebuffer_fetch_depth_stencil
+#define GL_ARM_shader_framebuffer_fetch_depth_stencil 1
+#endif /* GL_ARM_shader_framebuffer_fetch_depth_stencil */
+
+#ifndef GL_DMP_program_binary
+#define GL_DMP_program_binary 1
+#define GL_SMAPHS30_PROGRAM_BINARY_DMP 0x9251
+#define GL_SMAPHS_PROGRAM_BINARY_DMP 0x9252
+#define GL_DMP_PROGRAM_BINARY_DMP 0x9253
+#endif /* GL_DMP_program_binary */
+
#ifndef GL_DMP_shader_binary
#define GL_DMP_shader_binary 1
#define GL_SHADER_BINARY_DMP 0x9250
@@ -640,6 +796,14 @@
#define GL_UNSIGNED_NORMALIZED_EXT 0x8C17
#endif /* GL_EXT_color_buffer_half_float */
+#ifndef GL_EXT_copy_image
+#define GL_EXT_copy_image 1
+typedef void (GL_APIENTRYP PFNGLCOPYIMAGESUBDATAEXTPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glCopyImageSubDataEXT (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
+#endif
+#endif /* GL_EXT_copy_image */
+
#ifndef GL_EXT_debug_label
#define GL_EXT_debug_label 1
#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F
@@ -757,6 +921,30 @@
#endif
#endif /* GL_EXT_draw_buffers */
+#ifndef GL_EXT_draw_buffers_indexed
+#define GL_EXT_draw_buffers_indexed 1
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
+typedef void (GL_APIENTRYP PFNGLENABLEIEXTPROC) (GLenum target, GLuint index);
+typedef void (GL_APIENTRYP PFNGLDISABLEIEXTPROC) (GLenum target, GLuint index);
+typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONIEXTPROC) (GLuint buf, GLenum mode);
+typedef void (GL_APIENTRYP PFNGLBLENDEQUATIONSEPARATEIEXTPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha);
+typedef void (GL_APIENTRYP PFNGLBLENDFUNCIEXTPROC) (GLuint buf, GLenum src, GLenum dst);
+typedef void (GL_APIENTRYP PFNGLBLENDFUNCSEPARATEIEXTPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+typedef void (GL_APIENTRYP PFNGLCOLORMASKIEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);
+typedef GLboolean (GL_APIENTRYP PFNGLISENABLEDIEXTPROC) (GLenum target, GLuint index);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glEnableiEXT (GLenum target, GLuint index);
+GL_APICALL void GL_APIENTRY glDisableiEXT (GLenum target, GLuint index);
+GL_APICALL void GL_APIENTRY glBlendEquationiEXT (GLuint buf, GLenum mode);
+GL_APICALL void GL_APIENTRY glBlendEquationSeparateiEXT (GLuint buf, GLenum modeRGB, GLenum modeAlpha);
+GL_APICALL void GL_APIENTRY glBlendFunciEXT (GLuint buf, GLenum src, GLenum dst);
+GL_APICALL void GL_APIENTRY glBlendFuncSeparateiEXT (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+GL_APICALL void GL_APIENTRY glColorMaskiEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);
+GL_APICALL GLboolean GL_APIENTRY glIsEnablediEXT (GLenum target, GLuint index);
+#endif
+#endif /* GL_EXT_draw_buffers_indexed */
+
#ifndef GL_EXT_draw_instanced
#define GL_EXT_draw_instanced 1
typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount);
@@ -767,6 +955,57 @@
#endif
#endif /* GL_EXT_draw_instanced */
+#ifndef GL_EXT_geometry_point_size
+#define GL_EXT_geometry_point_size 1
+#endif /* GL_EXT_geometry_point_size */
+
+#ifndef GL_EXT_geometry_shader
+#define GL_EXT_geometry_shader 1
+#define GL_GEOMETRY_SHADER_EXT 0x8DD9
+#define GL_GEOMETRY_SHADER_BIT_EXT 0x00000004
+#define GL_GEOMETRY_LINKED_VERTICES_OUT_EXT 0x8916
+#define GL_GEOMETRY_LINKED_INPUT_TYPE_EXT 0x8917
+#define GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT 0x8918
+#define GL_GEOMETRY_SHADER_INVOCATIONS_EXT 0x887F
+#define GL_LAYER_PROVOKING_VERTEX_EXT 0x825E
+#ifndef GL_EXT_geometry_shader4
+#define GL_LINES_ADJACENCY_EXT 0x000A
+#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B
+#define GL_TRIANGLES_ADJACENCY_EXT 0x000C
+#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D
+#endif /* GL_EXT_geometry_shader4 */
+#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF
+#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT 0x8A2C
+#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8A32
+#define GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT 0x9123
+#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT 0x9124
+#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0
+#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1
+#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT 0x8E5A
+#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29
+#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT 0x92CF
+#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT 0x92D5
+#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT 0x90CD
+#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT 0x90D7
+#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D
+#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E
+#define GL_UNDEFINED_VERTEX_EXT 0x8260
+#define GL_PRIMITIVES_GENERATED_EXT 0x8C87
+#define GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT 0x9312
+#define GL_MAX_FRAMEBUFFER_LAYERS_EXT 0x9317
+#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8
+#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7
+#define GL_REFERENCED_BY_GEOMETRY_SHADER_EXT 0x9309
+typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level);
+#endif
+#endif /* GL_EXT_geometry_shader */
+
+#ifndef GL_EXT_gpu_shader5
+#define GL_EXT_gpu_shader5 1
+#endif /* GL_EXT_gpu_shader5 */
+
#ifndef GL_EXT_instanced_arrays
#define GL_EXT_instanced_arrays 1
#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_EXT 0x88FE
@@ -839,12 +1078,23 @@
#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A
#endif /* GL_EXT_occlusion_query_boolean */
+#ifndef GL_EXT_primitive_bounding_box
+#define GL_EXT_primitive_bounding_box 1
+#define GL_PRIMITIVE_BOUNDING_BOX_EXT 0x92BE
+typedef void (GL_APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXEXTPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glPrimitiveBoundingBoxEXT (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW);
+#endif
+#endif /* GL_EXT_primitive_bounding_box */
+
#ifndef GL_EXT_pvrtc_sRGB
#define GL_EXT_pvrtc_sRGB 1
#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54
#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55
#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56
#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57
+#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0
+#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1
#endif /* GL_EXT_pvrtc_sRGB */
#ifndef GL_EXT_read_format_bgra
@@ -992,10 +1242,25 @@
#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52
#endif /* GL_EXT_shader_framebuffer_fetch */
+#ifndef GL_EXT_shader_implicit_conversions
+#define GL_EXT_shader_implicit_conversions 1
+#endif /* GL_EXT_shader_implicit_conversions */
+
#ifndef GL_EXT_shader_integer_mix
#define GL_EXT_shader_integer_mix 1
#endif /* GL_EXT_shader_integer_mix */
+#ifndef GL_EXT_shader_io_blocks
+#define GL_EXT_shader_io_blocks 1
+#endif /* GL_EXT_shader_io_blocks */
+
+#ifndef GL_EXT_shader_pixel_local_storage
+#define GL_EXT_shader_pixel_local_storage 1
+#define GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT 0x8F63
+#define GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT 0x8F67
+#define GL_SHADER_PIXEL_LOCAL_STORAGE_EXT 0x8F64
+#endif /* GL_EXT_shader_pixel_local_storage */
+
#ifndef GL_EXT_shader_texture_lod
#define GL_EXT_shader_texture_lod 1
#endif /* GL_EXT_shader_texture_lod */
@@ -1008,6 +1273,109 @@
#define GL_SAMPLER_2D_SHADOW_EXT 0x8B62
#endif /* GL_EXT_shadow_samplers */
+#ifndef GL_EXT_tessellation_point_size
+#define GL_EXT_tessellation_point_size 1
+#endif /* GL_EXT_tessellation_point_size */
+
+#ifndef GL_EXT_tessellation_shader
+#define GL_EXT_tessellation_shader 1
+#define GL_PATCHES_EXT 0x000E
+#define GL_PATCH_VERTICES_EXT 0x8E72
+#define GL_TESS_CONTROL_OUTPUT_VERTICES_EXT 0x8E75
+#define GL_TESS_GEN_MODE_EXT 0x8E76
+#define GL_TESS_GEN_SPACING_EXT 0x8E77
+#define GL_TESS_GEN_VERTEX_ORDER_EXT 0x8E78
+#define GL_TESS_GEN_POINT_MODE_EXT 0x8E79
+#define GL_ISOLINES_EXT 0x8E7A
+#define GL_QUADS_EXT 0x0007
+#define GL_FRACTIONAL_ODD_EXT 0x8E7B
+#define GL_FRACTIONAL_EVEN_EXT 0x8E7C
+#define GL_MAX_PATCH_VERTICES_EXT 0x8E7D
+#define GL_MAX_TESS_GEN_LEVEL_EXT 0x8E7E
+#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT 0x8E7F
+#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT 0x8E80
+#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT 0x8E81
+#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT 0x8E82
+#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT 0x8E83
+#define GL_MAX_TESS_PATCH_COMPONENTS_EXT 0x8E84
+#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT 0x8E85
+#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT 0x8E86
+#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT 0x8E89
+#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT 0x8E8A
+#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT 0x886C
+#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT 0x886D
+#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT 0x8E1E
+#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT 0x8E1F
+#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT 0x92CD
+#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT 0x92CE
+#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT 0x92D3
+#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT 0x92D4
+#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT 0x90CB
+#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT 0x90CC
+#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT 0x90D8
+#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT 0x90D9
+#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221
+#define GL_IS_PER_PATCH_EXT 0x92E7
+#define GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT 0x9307
+#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT 0x9308
+#define GL_TESS_CONTROL_SHADER_EXT 0x8E88
+#define GL_TESS_EVALUATION_SHADER_EXT 0x8E87
+#define GL_TESS_CONTROL_SHADER_BIT_EXT 0x00000008
+#define GL_TESS_EVALUATION_SHADER_BIT_EXT 0x00000010
+typedef void (GL_APIENTRYP PFNGLPATCHPARAMETERIEXTPROC) (GLenum pname, GLint value);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glPatchParameteriEXT (GLenum pname, GLint value);
+#endif
+#endif /* GL_EXT_tessellation_shader */
+
+#ifndef GL_EXT_texture_border_clamp
+#define GL_EXT_texture_border_clamp 1
+#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004
+#define GL_CLAMP_TO_BORDER_EXT 0x812D
+typedef void (GL_APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params);
+typedef void (GL_APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params);
+typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params);
+typedef void (GL_APIENTRYP PFNGLSAMPLERPARAMETERIIVEXTPROC) (GLuint sampler, GLenum pname, const GLint *param);
+typedef void (GL_APIENTRYP PFNGLSAMPLERPARAMETERIUIVEXTPROC) (GLuint sampler, GLenum pname, const GLuint *param);
+typedef void (GL_APIENTRYP PFNGLGETSAMPLERPARAMETERIIVEXTPROC) (GLuint sampler, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVEXTPROC) (GLuint sampler, GLenum pname, GLuint *params);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params);
+GL_APICALL void GL_APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params);
+GL_APICALL void GL_APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params);
+GL_APICALL void GL_APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params);
+GL_APICALL void GL_APIENTRY glSamplerParameterIivEXT (GLuint sampler, GLenum pname, const GLint *param);
+GL_APICALL void GL_APIENTRY glSamplerParameterIuivEXT (GLuint sampler, GLenum pname, const GLuint *param);
+GL_APICALL void GL_APIENTRY glGetSamplerParameterIivEXT (GLuint sampler, GLenum pname, GLint *params);
+GL_APICALL void GL_APIENTRY glGetSamplerParameterIuivEXT (GLuint sampler, GLenum pname, GLuint *params);
+#endif
+#endif /* GL_EXT_texture_border_clamp */
+
+#ifndef GL_EXT_texture_buffer
+#define GL_EXT_texture_buffer 1
+#define GL_TEXTURE_BUFFER_EXT 0x8C2A
+#define GL_TEXTURE_BUFFER_BINDING_EXT 0x8C2A
+#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B
+#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C
+#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D
+#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_EXT 0x919F
+#define GL_SAMPLER_BUFFER_EXT 0x8DC2
+#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0
+#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8
+#define GL_IMAGE_BUFFER_EXT 0x9051
+#define GL_INT_IMAGE_BUFFER_EXT 0x905C
+#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067
+#define GL_TEXTURE_BUFFER_OFFSET_EXT 0x919D
+#define GL_TEXTURE_BUFFER_SIZE_EXT 0x919E
+typedef void (GL_APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer);
+typedef void (GL_APIENTRYP PFNGLTEXBUFFERRANGEEXTPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer);
+GL_APICALL void GL_APIENTRY glTexBufferRangeEXT (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
+#endif
+#endif /* GL_EXT_texture_buffer */
+
#ifndef GL_EXT_texture_compression_dxt1
#define GL_EXT_texture_compression_dxt1 1
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
@@ -1020,6 +1388,19 @@
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif /* GL_EXT_texture_compression_s3tc */
+#ifndef GL_EXT_texture_cube_map_array
+#define GL_EXT_texture_cube_map_array 1
+#define GL_TEXTURE_CUBE_MAP_ARRAY_EXT 0x9009
+#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_EXT 0x900A
+#define GL_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900C
+#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_EXT 0x900D
+#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900E
+#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_EXT 0x900F
+#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054
+#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F
+#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A
+#endif /* GL_EXT_texture_cube_map_array */
+
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_EXT_texture_filter_anisotropic 1
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
@@ -1082,6 +1463,19 @@
#define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368
#endif /* GL_EXT_texture_type_2_10_10_10_REV */
+#ifndef GL_EXT_texture_view
+#define GL_EXT_texture_view 1
+#define GL_TEXTURE_VIEW_MIN_LEVEL_EXT 0x82DB
+#define GL_TEXTURE_VIEW_NUM_LEVELS_EXT 0x82DC
+#define GL_TEXTURE_VIEW_MIN_LAYER_EXT 0x82DD
+#define GL_TEXTURE_VIEW_NUM_LAYERS_EXT 0x82DE
+#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF
+typedef void (GL_APIENTRYP PFNGLTEXTUREVIEWEXTPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glTextureViewEXT (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers);
+#endif
+#endif /* GL_EXT_texture_view */
+
#ifndef GL_EXT_unpack_subimage
#define GL_EXT_unpack_subimage 1
#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2
@@ -1138,6 +1532,52 @@
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG 0x9138
#endif /* GL_IMG_texture_compression_pvrtc2 */
+#ifndef GL_INTEL_performance_query
+#define GL_INTEL_performance_query 1
+#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000
+#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001
+#define GL_PERFQUERY_WAIT_INTEL 0x83FB
+#define GL_PERFQUERY_FLUSH_INTEL 0x83FA
+#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9
+#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0
+#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1
+#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2
+#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3
+#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4
+#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5
+#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8
+#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9
+#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA
+#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB
+#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC
+#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD
+#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE
+#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF
+#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500
+typedef void (GL_APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle);
+typedef void (GL_APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle);
+typedef void (GL_APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle);
+typedef void (GL_APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle);
+typedef void (GL_APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId);
+typedef void (GL_APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId);
+typedef void (GL_APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);
+typedef void (GL_APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten);
+typedef void (GL_APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId);
+typedef void (GL_APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle);
+GL_APICALL void GL_APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle);
+GL_APICALL void GL_APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle);
+GL_APICALL void GL_APIENTRY glEndPerfQueryINTEL (GLuint queryHandle);
+GL_APICALL void GL_APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId);
+GL_APICALL void GL_APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId);
+GL_APICALL void GL_APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue);
+GL_APICALL void GL_APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten);
+GL_APICALL void GL_APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId);
+GL_APICALL void GL_APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask);
+#endif
+#endif /* GL_INTEL_performance_query */
+
#ifndef GL_NV_blend_equation_advanced
#define GL_NV_blend_equation_advanced 1
#define GL_BLEND_OVERLAP_NV 0x9281
diff --git a/third_party/khronos/README.chromium b/third_party/khronos/README.chromium
index e7b4d86..d803f14 100644
--- a/third_party/khronos/README.chromium
+++ b/third_party/khronos/README.chromium
@@ -2,7 +2,7 @@
Short Name: khronos_headers
URL: http://www.khronos.org/registry
Version: unknown
-Date: 2013-12-13
+Date: 2014-10-20
License: MIT/X11, SGI Free Software License B
Security Critical: no
@@ -21,6 +21,13 @@
GLES2/gl2.h
- Added include of gl2chromium.h
+GLES2/gl2ext.h
+ - Chromium-specific define to account for issues in gles2_conform_test,
+ see http://crbug.com/329708.
+ - Commented out GL_SAMPLER, conflicts with third_party/gles2_conform
+ - Drop const from last argument of GLDEBUGPROCKHR (win trybot fail)
+ - Added ifdef around GL_LINES_ADJACENCY_EXT to fix conflict with OSX
+ OpenGL framework headers.
EGL/eglplatform.h
- Added EGLNative*Type for Mac.
- Added EGLNative*Type for native linux framebuffers.
diff --git a/tools/android/checkstyle/chromium-style-5.0.xml b/tools/android/checkstyle/chromium-style-5.0.xml
index 351b5b3..46b19e9 100644
--- a/tools/android/checkstyle/chromium-style-5.0.xml
+++ b/tools/android/checkstyle/chromium-style-5.0.xml
@@ -175,6 +175,16 @@
<property name="option" value="eol"/>
<property name="tokens" value="ASSIGN"/>
</module>
+ <module name="SeparatorWrap">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="DOT"/>
+ <property name="option" value="nl"/>
+ </module>
+ <module name="SeparatorWrap">
+ <property name="severity" value="warning"/>
+ <property name="tokens" value="COMMA"/>
+ <property name="option" value="EOL"/>
+ </module>
</module>
<module name="FileTabCharacter">
<property name="severity" value="error"/>
diff --git a/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java b/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java
index 4d3d03f..45e0eb8 100644
--- a/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java
+++ b/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java
@@ -40,12 +40,12 @@
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification =
- new Notification.Builder(getApplicationContext()).
- setContentTitle("MC running (" + memory + "Mb)").
- setSmallIcon(R.drawable.notification_icon).
- setDeleteIntent(pendingIntent).
- setContentIntent(pendingIntent).
- build();
+ new Notification.Builder(getApplicationContext())
+ .setContentTitle("MC running (" + memory + "Mb)")
+ .setSmallIcon(R.drawable.notification_icon)
+ .setDeleteIntent(pendingIntent)
+ .setContentIntent(pendingIntent)
+ .build();
startForeground(RESIDENT_NOTIFICATION_ID, notification);
mIsInForeground = true;
}
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp
index 3ff133d..a608cd2 100644
--- a/tools/clang/plugins/FindBadConstructsConsumer.cpp
+++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp
@@ -66,6 +66,10 @@
return type;
}
+bool IsGtestTestFixture(const CXXRecordDecl* decl) {
+ return decl->getQualifiedNameAsString() == "testing::Test";
+}
+
FixItHint FixItRemovalForVirtual(const SourceManager& manager,
const CXXMethodDecl* method) {
// Unfortunately, there doesn't seem to be a good way to determine the
@@ -294,7 +298,12 @@
++i) {
const CXXMethodDecl* overridden = *i;
if (IsMethodInBannedOrTestingNamespace(overridden) ||
- InTestingNamespace(overridden)) {
+ // Provide an exception for ::testing::Test. gtest itself uses some
+ // magic to try to make sure SetUp()/TearDown() aren't capitalized
+ // incorrectly, but having the plugin enforce override is also nice.
+ (InTestingNamespace(overridden) &&
+ (!options_.strict_virtual_specifiers ||
+ !IsGtestTestFixture(overridden->getParent())))) {
return true;
}
}
diff --git a/tools/clang/plugins/tests/virtual_specifiers.cpp b/tools/clang/plugins/tests/virtual_specifiers.cpp
index f4479a8..4d9acd3 100644
--- a/tools/clang/plugins/tests/virtual_specifiers.cpp
+++ b/tools/clang/plugins/tests/virtual_specifiers.cpp
@@ -61,3 +61,33 @@
~OverrideAndFinal() OVERRIDE FINAL {}
void F() OVERRIDE FINAL {}
};
+
+// Finally, some simple sanity tests that overrides in the testing namespace
+// don't trigger warnings, except for testing::Test.
+namespace testing {
+
+class Test {
+ public:
+ virtual ~Test();
+ virtual void SetUp();
+};
+
+class NotTest {
+ public:
+ virtual ~NotTest();
+ virtual void SetUp();
+};
+
+} // namespace
+
+class MyTest : public testing::Test {
+ public:
+ virtual ~MyTest();
+ virtual void SetUp() override;
+};
+
+class MyNotTest : public testing::NotTest {
+ public:
+ virtual ~MyNotTest();
+ virtual void SetUp() override;
+};
diff --git a/tools/clang/plugins/tests/virtual_specifiers.txt b/tools/clang/plugins/tests/virtual_specifiers.txt
index 2ad1420..1f2e475 100644
--- a/tools/clang/plugins/tests/virtual_specifiers.txt
+++ b/tools/clang/plugins/tests/virtual_specifiers.txt
@@ -48,4 +48,11 @@
virtual_specifiers.cpp:10:18: note: expanded from macro 'OVERRIDE'
#define OVERRIDE override
^
-12 warnings generated.
+virtual_specifiers.cpp:85:20: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+ virtual ~MyTest();
+ ^
+ override
+virtual_specifiers.cpp:86:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
+ virtual void SetUp() override;
+ ^~~~~~~~
+14 warnings generated.
diff --git a/tools/relocation_packer/BUILD.gn b/tools/relocation_packer/BUILD.gn
new file mode 100644
index 0000000..d841277
--- /dev/null
+++ b/tools/relocation_packer/BUILD.gn
@@ -0,0 +1,135 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("config.gni")
+
+assert(relocation_packing_supported)
+
+if (target_arch == "arm") {
+ target_define = "TARGET_ARM"
+} else if (target_arch == "arm64") {
+ target_define = "TARGET_ARM64"
+}
+
+if (current_toolchain == host_toolchain) {
+ # GYP: //tools/relocation_packer/relocation_packer.gyp:lib_relocation_packer
+ source_set("lib_relocation_packer") {
+ defines = [ target_define ]
+ deps = [ "//third_party/elfutils:libelf" ]
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+ sources = [
+ "src/debug.cc",
+ "src/delta_encoder.cc",
+ "src/elf_file.cc",
+ "src/leb128.cc",
+ "src/packer.cc",
+ "src/sleb128.cc",
+ "src/run_length_encoder.cc",
+ ]
+ }
+
+ # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer
+ executable("relocation_packer") {
+ defines = [ target_define ]
+ deps = [
+ ":lib_relocation_packer",
+ "//third_party/elfutils:libelf",
+ ]
+ sources = [ "src/main.cc" ]
+ }
+
+ # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests
+ test("relocation_packer_unittests") {
+ sources = [
+ "src/debug_unittest.cc",
+ "src/delta_encoder_unittest.cc",
+ "src/elf_file_unittest.cc",
+ "src/leb128_unittest.cc",
+ "src/packer_unittest.cc",
+ "src/sleb128_unittest.cc",
+ "src/run_length_encoder_unittest.cc",
+ "src/run_all_unittests.cc",
+ ]
+ rebased_test_data = rebase_path("test_data", root_build_dir)
+ data = [
+ "test_data/elf_file_unittest_relocs_arm32.so",
+ "test_data/elf_file_unittest_relocs_arm32_packed.so",
+ "test_data/elf_file_unittest_relocs_arm64.so",
+ "test_data/elf_file_unittest_relocs_arm64_packed.so",
+ ]
+ defines = [
+ target_define,
+ "INTERMEDIATE_DIR=\"$rebased_test_data\""
+ ]
+ include_dirs = [ "//" ]
+ deps = [
+ ":lib_relocation_packer",
+ ":relocation_packer_test_data",
+ "//testing:gtest",
+ ]
+ }
+}
+
+if (current_toolchain == default_toolchain &&
+ (target_arch == "arm" || target_arch == "arm64")) {
+ # Targets to build test data. These participate only in building test
+ # data for use with elf_file_unittest.cc, and are not part of the main
+ # relocation packer build. Unit test data files are checked in to the
+ # source tree as 'golden' data, and are not generated 'on the fly' by
+ # the build.
+ #
+ # See test_data/generate_elf_file_unittest_relocs.sh for instructions.
+
+ # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_test_data
+ shared_library("relocation_packer_test_data") {
+ cflags = [ "-O0", "-g0" ]
+ sources = [
+ "test_data/elf_file_unittest_relocs.cc"
+ ]
+ }
+
+ # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests_test_data
+ action("relocation_packer_unittests_test_data") {
+ script = "test_data/generate_elf_file_unittest_relocs.py"
+ test_file = "$root_build_dir/librelocation_packer_test_data.so"
+ if (target_arch == "arm") {
+ added_section = ".android.rel.dyn"
+ packed_output = "elf_file_unittest_relocs_arm32_packed.so"
+ unpacked_output = "elf_file_unittest_relocs_arm32.so"
+ } else if (target_arch == "arm64") {
+ added_section = ".android.rela.dyn"
+ packed_output = "elf_file_unittest_relocs_arm64_packed.so"
+ unpacked_output = "elf_file_unittest_relocs_arm64.so"
+ } else {
+ assert(false, "Unsupported target arch for relocation packer")
+ }
+
+ packed_output = "$root_build_dir/$packed_output"
+ unpacked_output = "$root_build_dir/$unpacked_output"
+
+ inputs = [
+ test_file,
+ ]
+
+ deps = [
+ ":relocation_packer_test_data",
+ ":relocation_packer($host_toolchain)",
+ ]
+
+ outputs = [
+ packed_output,
+ unpacked_output,
+ ]
+
+ args = [
+ "--android-pack-relocations", rebase_path(relocation_packer_exe, root_build_dir),
+ "--android-objcopy", rebase_path(android_objcopy, root_build_dir),
+ "--added-section=$added_section",
+ "--test-file", rebase_path(test_file, root_build_dir),
+ "--packed-output", rebase_path(packed_output, root_build_dir),
+ "--unpacked-output", rebase_path(unpacked_output, root_build_dir),
+ ]
+ }
+}
diff --git a/tools/relocation_packer/LICENSE b/tools/relocation_packer/LICENSE
new file mode 100644
index 0000000..972bb2e
--- /dev/null
+++ b/tools/relocation_packer/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/relocation_packer/README.TXT b/tools/relocation_packer/README.TXT
new file mode 100644
index 0000000..071ab5d
--- /dev/null
+++ b/tools/relocation_packer/README.TXT
@@ -0,0 +1,135 @@
+Introduction:
+-------------
+
+Relative relocations are the bulk of dynamic relocations (the .rel.dyn
+or .rela.dyn sections) in libchrome.<version>.so. The ELF standard
+representation of them is wasteful.
+
+Packing uses a combination of run length encoding, delta encoding, and LEB128
+encoding to store them more efficiently. Packed relocations are placed in
+a new .android.rel.dyn or .android.rela.dyn section. Packing reduces
+the footprint of libchrome.<version>.so in the filesystem, in APK downloads,
+and in memory when loaded on the device.
+
+A packed libchrome.<version>.so is designed so that it can be loaded directly
+on Android, but requires the explicit support of a crazy linker that has been
+extended to understand packed relocations. Packed relocations are currently
+only supported on ARM.
+
+A packed libchrome.<version>.so cannot currently be used with the standard
+Android runtime linker.
+
+See src/*.h for design and implementation notes.
+
+
+Notes:
+------
+
+Packing does not adjust debug data. An unstripped libchrome.<version>.so
+can be packed and will run, but may no longer be useful for debugging.
+
+Unpacking on the device requires the explicit support of an extended crazy
+linker. Adds the following new .dynamic tags, used by the crazy linker to
+find the packed .android.rel.dyn or .android.rela.dyn section data:
+
+ DT_ANDROID_REL_OFFSET = DT_LOOS (Operating System specific: 0x6000000d)
+ - The offset of packed relocation data in libchrome.<version>.so
+ DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e)
+ - The size of packed relocation data in bytes
+
+32 bit ARM libraries use relocations without addends. 64 bit ARM libraries
+use relocations with addends. The packing strategy necessarily differs for
+the two relocation types.
+
+Where libchrome.<version>.so contains relocations without addends, the format
+of .android.rel.dyn data is:
+
+ "APR1" identifier
+ N: the number of count-delta pairs in the encoding
+ A: the initial offset
+ N * C,D: N count-delta pairs
+
+Where libchrome.<version>.so contains relocations with addends, the format
+of .android.rela.dyn data is:
+
+ "APA1" identifier
+ N: the number of addr-addend delta pairs in the encoding
+ N * A,V: N addr-addend delta pairs
+
+All numbers in the encoding stream are stored as LEB128 values. For details
+see http://en.wikipedia.org/wiki/LEB128.
+
+The streaming unpacking algorithm for 32 bit ARM is:
+
+ skip over "APR1"
+ pairs, addr = next leb128 value, next leb128 value
+ emit R_ARM_RELATIVE relocation with r_offset = addr
+ while pairs:
+ count, delta = next leb128 value, next leb128 value
+ while count:
+ addr += delta
+ emit R_ARM_RELATIVE relocation with r_offset = addr
+ count--
+ pairs--
+
+The streaming unpacking algorithm for 64 bit ARM is:
+
+ skip over "APA1"
+ pairs = next signed leb128 value
+ addr, addend = 0, 0
+ while pairs:
+ addr += next signed leb128 value
+ addend += next signed leb128 value
+ emit R_AARCH64_RELATIVE relocation with r_offset = addr, r_addend = addend
+ pairs--
+
+
+Usage instructions:
+-------------------
+
+To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and
+then run the tool:
+
+ echo -n 'NULL' >/tmp/small
+ if file libchrome.<version>.so | grep -q 'ELF 32'; then
+ arm-linux-androideabi-objcopy
+ --add-section .android.rel.dyn=/tmp/small
+ libchrome.<version>.so libchrome.<version>.so.packed
+ else
+ aarch64-linux-android-objcopy
+ --add-section .android.rela.dyn=/tmp/small
+ libchrome.<version>.so libchrome.<version>.so.packed
+ fi
+ rm /tmp/small
+ relocation_packer libchrome.<version>.so.packed
+
+To unpack and restore the shared library to its original state:
+
+ cp libchrome.<version>.so.packed unpackable
+ relocation_packer -u unpackable
+ if file libchrome.<version>.so | grep -q 'ELF 32'; then
+ arm-linux-androideabi-objcopy \
+ --remove-section=.android.rel.dyn unpackable libchrome.<version>.so
+ else
+ aarch64-linux-android-objcopy \
+ --remove-section=.android.rela.dyn unpackable libchrome.<version>.so
+ endif
+ rm unpackable
+
+
+Bugs & TODOs:
+-------------
+
+Requires two free slots in the .dynamic section. Uses these to add data that
+tells the crazy linker where to find the packed relocation data. Fails
+if insufficient free slots exist (use gold --spare-dynamic-slots to increase
+the allocation).
+
+Requires libelf 0.158 or later. Earlier libelf releases may be buggy in
+ways that prevent the packer from working correctly.
+
+
+Testing:
+--------
+
+Unittests run under gtest, on the host system.
diff --git a/tools/relocation_packer/config.gni b/tools/relocation_packer/config.gni
new file mode 100644
index 0000000..fec2752
--- /dev/null
+++ b/tools/relocation_packer/config.gni
@@ -0,0 +1,22 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+relocation_packing_supported = (
+ (target_arch == "arm") || (target_arch == "arm64"))
+
+if (relocation_packing_supported) {
+ relocation_packer_target = "//tools/relocation_packer($host_toolchain)"
+ relocation_packer_dir = get_label_info(
+ "$relocation_packer_target", "root_out_dir")
+ relocation_packer_exe = "${relocation_packer_dir}/relocation_packer"
+
+ if (target_arch == "arm") {
+ relocations_have_addends = 0
+ } else if (target_arch == "arm64") {
+ relocations_have_addends = 1
+ }
+} else {
+ relocations_have_addends = 0
+ relocation_packer_exe = ""
+}
diff --git a/tools/relocation_packer/relocation_packer.gyp b/tools/relocation_packer/relocation_packer.gyp
new file mode 100644
index 0000000..1e9c1b9
--- /dev/null
+++ b/tools/relocation_packer/relocation_packer.gyp
@@ -0,0 +1,161 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'target_define%': 'TARGET_UNSUPPORTED',
+ 'conditions': [
+ [ 'target_arch == "arm"', {
+ 'target_define': 'TARGET_ARM',
+ }],
+ [ 'target_arch == "arm64"', {
+ 'target_define': 'TARGET_ARM64',
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ # GN: //tools/relocation_packer:lib_relocation_packer
+ 'target_name': 'lib_relocation_packer',
+ 'toolsets': ['host'],
+ 'type': 'static_library',
+ 'defines': [
+ '<(target_define)',
+ ],
+ 'dependencies': [
+ '../../third_party/elfutils/elfutils.gyp:libelf',
+ ],
+ 'sources': [
+ 'src/debug.cc',
+ 'src/delta_encoder.cc',
+ 'src/elf_file.cc',
+ 'src/leb128.cc',
+ 'src/packer.cc',
+ 'src/sleb128.cc',
+ 'src/run_length_encoder.cc',
+ ],
+ },
+ {
+ # GN: //tools/relocation_packer:relocation_packer
+ 'target_name': 'relocation_packer',
+ 'toolsets': ['host'],
+ 'type': 'executable',
+ 'defines': [
+ '<(target_define)',
+ ],
+ 'dependencies': [
+ '../../third_party/elfutils/elfutils.gyp:libelf',
+ 'lib_relocation_packer',
+ ],
+ 'sources': [
+ 'src/main.cc',
+ ],
+ },
+ {
+ # GN: //tools/relocation_packer:relocation_packer_unittests
+ 'target_name': 'relocation_packer_unittests',
+ 'toolsets': ['host'],
+ 'type': 'executable',
+ 'defines': [
+ '<(target_define)',
+ ],
+ 'cflags': [
+ '-DINTERMEDIATE_DIR="<(INTERMEDIATE_DIR)"',
+ ],
+ 'dependencies': [
+ '../../testing/gtest.gyp:gtest',
+ 'lib_relocation_packer',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'src/debug_unittest.cc',
+ 'src/delta_encoder_unittest.cc',
+ 'src/elf_file_unittest.cc',
+ 'src/leb128_unittest.cc',
+ 'src/packer_unittest.cc',
+ 'src/sleb128_unittest.cc',
+ 'src/run_length_encoder_unittest.cc',
+ 'src/run_all_unittests.cc',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(INTERMEDIATE_DIR)',
+ 'files': [
+ 'test_data/elf_file_unittest_relocs_arm32.so',
+ 'test_data/elf_file_unittest_relocs_arm32_packed.so',
+ 'test_data/elf_file_unittest_relocs_arm64.so',
+ 'test_data/elf_file_unittest_relocs_arm64_packed.so',
+ ],
+ },
+ ],
+ },
+
+ # Targets to build test data. These participate only in building test
+ # data for use with elf_file_unittest.cc, and are not part of the main
+ # relocation packer build. Unit test data files are checked in to the
+ # source tree as 'golden' data, and are not generated 'on the fly' by
+ # the build.
+ #
+ # See test_data/generate_elf_file_unittest_relocs.sh for instructions.
+ {
+ # GN: //tools/relocation_packer:relocation_packer_test_data
+ 'target_name': 'relocation_packer_test_data',
+ 'toolsets': ['target'],
+ 'type': 'shared_library',
+ 'cflags': [
+ '-O0',
+ '-g0',
+ ],
+ 'sources': [
+ 'test_data/elf_file_unittest_relocs.cc',
+ ],
+ },
+ {
+ # GN: //tools/relocation_packer:relocation_packer_unittests_test_data
+ 'target_name': 'relocation_packer_unittests_test_data',
+ 'toolsets': ['target'],
+ 'type': 'none',
+ 'actions': [
+ {
+ 'variables': {
+ 'test_file': '<(SHARED_LIB_DIR)/librelocation_packer_test_data.so',
+ 'conditions': [
+ [ 'target_arch == "arm"', {
+ 'added_section': '.android.rel.dyn',
+ 'unpacked_output': 'elf_file_unittest_relocs_arm32.so',
+ 'packed_output': 'elf_file_unittest_relocs_arm32_packed.so',
+ }],
+ [ 'target_arch == "arm64"', {
+ 'added_section': '.android.rela.dyn',
+ 'unpacked_output': 'elf_file_unittest_relocs_arm64.so',
+ 'packed_output': 'elf_file_unittest_relocs_arm64_packed.so',
+ }],
+ ],
+ },
+ 'action_name': 'generate_relocation_packer_test_data',
+ 'inputs': [
+ 'test_data/generate_elf_file_unittest_relocs.py',
+ '<(PRODUCT_DIR)/relocation_packer',
+ '<(test_file)',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/<(unpacked_output)',
+ '<(INTERMEDIATE_DIR)/<(packed_output)',
+ ],
+ 'action': [
+ 'python', 'test_data/generate_elf_file_unittest_relocs.py',
+ '--android-pack-relocations=<(PRODUCT_DIR)/relocation_packer',
+ '--android-objcopy=<(android_objcopy)',
+ '--added-section=<(added_section)',
+ '--test-file=<(test_file)',
+ '--unpacked-output=<(INTERMEDIATE_DIR)/<(unpacked_output)',
+ '--packed-output=<(INTERMEDIATE_DIR)/<(packed_output)',
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/tools/relocation_packer/src/debug.cc b/tools/relocation_packer/src/debug.cc
new file mode 100644
index 0000000..29d7ab0
--- /dev/null
+++ b/tools/relocation_packer/src/debug.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug.h"
+
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+
+namespace relocation_packer {
+
+// Construct a new message logger. Prints if level is less than or equal to
+// the level set with SetVerbose() and predicate is true.
+Logger::Logger(Severity severity, int level, bool predicate) {
+ severity_ = severity;
+ level_ = level;
+ predicate_ = predicate;
+}
+
+// On destruction, flush and print the strings accumulated. Abort if FATAL.
+Logger::~Logger() {
+ if (predicate_) {
+ if (level_ <= max_level_) {
+ std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_;
+ std::string tag;
+ switch (severity_) {
+ case INFO: tag = "INFO"; break;
+ case WARNING: tag = "WARNING"; break;
+ case ERROR: tag = "ERROR"; break;
+ case FATAL: tag = "FATAL"; break;
+ }
+ stream_.flush();
+ *log << tag << ": " << stream_.str() << std::endl;
+ }
+ if (severity_ == FATAL)
+ abort();
+ }
+}
+
+// Reset to initial state.
+void Logger::Reset() {
+ max_level_ = -1;
+ info_stream_ = &std::cout;
+ error_stream_ = &std::cerr;
+}
+
+// Verbosity. Not thread-safe.
+int Logger::max_level_ = -1;
+
+// Logging streams. Not thread-safe.
+std::ostream* Logger::info_stream_ = &std::cout;
+std::ostream* Logger::error_stream_ = &std::cerr;
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/debug.h b/tools/relocation_packer/src/debug.h
new file mode 100644
index 0000000..48be6c1
--- /dev/null
+++ b/tools/relocation_packer/src/debug.h
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Logging and checks. Avoids a dependency on base.
+//
+// LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL.
+// INFO prints to stdout, the others to stderr. FATAL aborts after printing.
+//
+// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent.
+//
+// VLOG(level) logs INFO messages where level is less than or equal to the
+// verbosity level set with SetVerbose().
+//
+// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true,
+// else silent.
+//
+// CHECK(predicate) logs a FATAL error if predicate is false.
+// NOTREACHED() always aborts.
+// Log streams can be changed with SetStreams(). Logging is not thread-safe.
+//
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
+#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
+
+#include <limits.h>
+#include <ostream>
+#include <sstream>
+
+namespace relocation_packer {
+
+class Logger {
+ public:
+ enum Severity {INFO = 0, WARNING, ERROR, FATAL};
+
+ // Construct a new message logger. Prints if level is less than or
+ // equal to the level set with SetVerbose() and predicate is true.
+ // |severity| is an enumerated severity.
+ // |level| is the verbosity level.
+ // |predicate| controls if the logger prints or is silent.
+ Logger(Severity severity, int level, bool predicate);
+
+ // On destruction, flush and print the strings accumulated in stream_.
+ ~Logger();
+
+ // Return the stream for this logger.
+ std::ostream& GetStream() { return stream_; }
+
+ // Set verbosity level. Messages with a level less than or equal to
+ // this level are printed, others are discarded. Static, not thread-safe.
+ static void SetVerbose(int level) { max_level_ = level; }
+
+ // Set info and error logging streams. Static, not thread-safe.
+ static void SetStreams(std::ostream* info_stream,
+ std::ostream* error_stream) {
+ info_stream_ = info_stream;
+ error_stream_ = error_stream;
+ }
+
+ // Reset to initial state.
+ static void Reset();
+
+ private:
+ // Message severity, verbosity level, and predicate.
+ Severity severity_;
+ int level_;
+ bool predicate_;
+
+ // String stream, accumulates message text.
+ std::ostringstream stream_;
+
+ // Verbosity for INFO messages. Not thread-safe.
+ static int max_level_;
+
+ // Logging streams. Not thread-safe.
+ static std::ostream* info_stream_;
+ static std::ostream* error_stream_;
+};
+
+} // namespace relocation_packer
+
+// Make logging severities visible globally.
+typedef relocation_packer::Logger::Severity LogSeverity;
+using LogSeverity::INFO;
+using LogSeverity::WARNING;
+using LogSeverity::ERROR;
+using LogSeverity::FATAL;
+
+// LOG(severity) prints a message with the given severity, and aborts if
+// severity is FATAL. LOG_IF(severity, predicate) does the same but only if
+// predicate is true. INT_MIN is guaranteed to be less than or equal to
+// any verbosity level.
+#define LOG(severity) \
+ (relocation_packer::Logger(severity, INT_MIN, true).GetStream())
+#define LOG_IF(severity, predicate) \
+ (relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream())
+
+// VLOG(level) prints its message as INFO if level is less than or equal to
+// the current verbosity level.
+#define VLOG(level) \
+ (relocation_packer::Logger(INFO, (level), true).GetStream())
+#define VLOG_IF(level, predicate) \
+ (relocation_packer::Logger(INFO, (level), (predicate)).GetStream())
+
+// CHECK(predicate) fails with a FATAL log message if predicate is false.
+#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << __FUNCTION__ << ": CHECK '" #predicate "' failed")
+
+// NOTREACHED() always fails with a FATAL log message.
+#define NOTREACHED(_) (LOG(FATAL) \
+ << __FILE__ << ":" << __LINE__ << ": " \
+ << __FUNCTION__ << ": NOTREACHED() hit")
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
diff --git a/tools/relocation_packer/src/debug_unittest.cc b/tools/relocation_packer/src/debug_unittest.cc
new file mode 100644
index 0000000..1b65cd1
--- /dev/null
+++ b/tools/relocation_packer/src/debug_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "debug.h"
+
+#include <sstream>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Debug, Log) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ LOG(INFO) << "INFO log message";
+ LOG(WARNING) << "WARNING log message";
+ LOG(ERROR) << "ERROR log message";
+
+ EXPECT_EQ("INFO: INFO log message\n", info.str());
+ EXPECT_EQ("WARNING: WARNING log message\n"
+ "ERROR: ERROR log message\n", error.str());
+ Logger::Reset();
+}
+
+TEST(Debug, LogIf) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ LOG_IF(INFO, true) << "INFO log message";
+ LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT";
+ LOG_IF(WARNING, true) << "WARNING log message";
+ LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT";
+ LOG_IF(ERROR, true) << "ERROR log message";
+ LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT";
+ LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("INFO: INFO log message\n", info.str());
+ EXPECT_EQ("WARNING: WARNING log message\n"
+ "ERROR: ERROR log message\n", error.str());
+ Logger::Reset();
+}
+
+TEST(Debug, Vlog) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+ VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+ VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("", info.str());
+ EXPECT_EQ("", error.str());
+
+ Logger::SetVerbose(1);
+
+ VLOG(0) << "VLOG 0 INFO log message";
+ VLOG(1) << "VLOG 1 INFO log message";
+ VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
+ "INFO: VLOG 1 INFO log message\n", info.str());
+ EXPECT_EQ("", error.str());
+ Logger::Reset();
+}
+
+TEST(Debug, VlogIf) {
+ Logger::Reset();
+ std::ostringstream info;
+ std::ostringstream error;
+ Logger::SetStreams(&info, &error);
+
+ VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("", info.str());
+ EXPECT_EQ("", error.str());
+
+ Logger::SetVerbose(1);
+
+ VLOG_IF(0, true) << "VLOG 0 INFO log message";
+ VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(1, true) << "VLOG 1 INFO log message";
+ VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+ VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
+
+ EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
+ "INFO: VLOG 1 INFO log message\n", info.str());
+ EXPECT_EQ("", error.str());
+ Logger::Reset();
+}
+
+TEST(DebugDeathTest, Fatal) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Logger::Reset();
+ EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message");
+ EXPECT_DEATH(
+ LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message");
+}
+
+TEST(DebugDeathTest, Check) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Logger::Reset();
+ CHECK(0 == 0);
+ EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed");
+}
+
+TEST(DebugDeathTest, NotReached) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Logger::Reset();
+ EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit");
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/delta_encoder.cc b/tools/relocation_packer/src/delta_encoder.cc
new file mode 100644
index 0000000..69cc91a
--- /dev/null
+++ b/tools/relocation_packer/src/delta_encoder.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "delta_encoder.h"
+
+#include <vector>
+
+#include "debug.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode relative relocations with addends into a delta encoded (packed)
+// representation. Represented as simple r_offset and r_addend delta pairs,
+// with an implicit neutral element at the start.
+void RelocationDeltaCodec::Encode(const std::vector<ELF::Rela>& relocations,
+ std::vector<ELF::Sxword>* packed) {
+ // One relocation is sufficient for delta encoding.
+ if (relocations.size() < 1)
+ return;
+
+ // Start with the element count, then append the delta pairs.
+ packed->push_back(relocations.size());
+
+ ELF::Addr offset = 0;
+ ELF::Sxword addend = 0;
+
+ for (size_t i = 0; i < relocations.size(); ++i) {
+ const ELF::Rela* relocation = &relocations[i];
+ CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode);
+
+ packed->push_back(relocation->r_offset - offset);
+ offset = relocation->r_offset;
+ packed->push_back(relocation->r_addend - addend);
+ addend = relocation->r_addend;
+ }
+}
+
+// Decode relative relocations with addends from a delta encoded (packed)
+// representation.
+void RelocationDeltaCodec::Decode(const std::vector<ELF::Sxword>& packed,
+ std::vector<ELF::Rela>* relocations) {
+ // We need at least one packed pair after the packed pair count to be
+ // able to unpack.
+ if (packed.size() < 3)
+ return;
+
+ // Ensure that the packed data offers enough pairs. There may be zero
+ // padding on it that we ignore.
+ CHECK(static_cast<size_t>(packed[0]) <= (packed.size() - 1) >> 1);
+
+ ELF::Addr offset = 0;
+ ELF::Sxword addend = 0;
+
+ // The first packed vector element is the pairs count. Start uncondensing
+ // pairs at the second, and finish at the end of the pairs data.
+ const size_t pairs_count = packed[0];
+ for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) {
+ offset += packed[i];
+ addend += packed[i + 1];
+
+ // Generate a relocation for this offset and addend pair.
+ ELF::Rela relocation;
+ relocation.r_offset = offset;
+ relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocation.r_addend = addend;
+ relocations->push_back(relocation);
+ }
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/delta_encoder.h b/tools/relocation_packer/src/delta_encoder.h
new file mode 100644
index 0000000..498b6d1
--- /dev/null
+++ b/tools/relocation_packer/src/delta_encoder.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Delta encode and decode relative relocations with addends.
+//
+// Relative relocations are the bulk of dynamic relocations (the
+// .rel.dyn or .rela.dyn sections) in libchrome.<version>.so, and the ELF
+// standard representation of them is wasteful. .rel.dyn contains
+// relocations without addends, .rela.dyn relocations with addends.
+//
+// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes
+// on 64 bit plaforms. It is split into offset, info, and addend fields.
+// Offsets strictly increase, and each is commonly a few bytes different
+// from its predecessor. Addends are less well behaved. The info field is
+// constant. Example, from 'readelf -x4 libchrome.<version>.so' 64 bit:
+//
+// offset info
+// 80949303 00000000 03040000 00000000 ................
+// addend offset
+// fc015b00 00000000 88949303 00000000 ..[.............
+// info addend
+// 03040000 00000000 24025b00 00000000 ........$.[.....
+// offset info
+// 90949303 00000000 03040000 00000000 ................
+// addend offset
+// 3c025b00 00000000 98949303 00000000 <.[.............
+// info addend
+// 03040000 00000000 50025b00 00000000 ........P.[.....
+//
+// The offset strictly increases, but the addend is unpredictable, so run
+// length encoding will not work well with this data. We can however pack
+// with delta encoding. The upper four bytes of the eight byte offset and
+// addend are invariably zeroes. The difference between adjacent offsets
+// is almost always small, and between adjacent addends is often small. And
+// info is constant and can be eliminated.
+//
+// Delta encoding reduces the size of the data modestly, so that the first
+// three relocations above can be represented as:
+//
+// initial offset initial addend offset delta addend delta
+// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028
+// offset delta addend delta ...
+// 00000000 00000008 00000000 0000009f
+//
+// The addend delta can be negative as well as positive, but overall the
+// deltas have a much smaller range than the input data. When encoded as
+// signed LEB128 the total data reduction becomes useful.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
+
+#include <vector>
+
+#include "elf.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// A RelocationDeltaCodec packs vectors of relative relocations with
+// addends into more compact forms, and unpacks them to reproduce the
+// pre-packed data.
+class RelocationDeltaCodec {
+ public:
+ // Encode relative relocations with addends into a more compact form.
+ // |relocations| is a vector of relative relocation with addend structs.
+ // |packed| is the vector of packed words into which relocations are packed.
+ static void Encode(const std::vector<ELF::Rela>& relocations,
+ std::vector<ELF::Sxword>* packed);
+
+ // Decode relative relocations with addends from their more compact form.
+ // |packed| is the vector of packed relocations.
+ // |relocations| is a vector of unpacked relative relocations.
+ static void Decode(const std::vector<ELF::Sxword>& packed,
+ std::vector<ELF::Rela>* relocations);
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_
diff --git a/tools/relocation_packer/src/delta_encoder_unittest.cc b/tools/relocation_packer/src/delta_encoder_unittest.cc
new file mode 100644
index 0000000..b9bf39a
--- /dev/null
+++ b/tools/relocation_packer/src/delta_encoder_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "delta_encoder.h"
+
+#include <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AddRelocation(ELF::Addr addr,
+ ELF::Sxword addend,
+ std::vector<ELF::Rela>* relocations) {
+ ELF::Rela relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocation.r_addend = addend;
+ relocations->push_back(relocation);
+}
+
+bool CheckRelocation(ELF::Addr addr,
+ ELF::Sxword addend,
+ const ELF::Rela& relocation) {
+ return relocation.r_offset == addr &&
+ ELF_R_SYM(relocation.r_info) == 0 &&
+ ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
+ relocation.r_addend == addend;
+}
+
+} // namespace
+
+namespace relocation_packer {
+
+TEST(Delta, Encode) {
+ std::vector<ELF::Rela> relocations;
+ std::vector<ELF::Sxword> packed;
+
+ RelocationDeltaCodec codec;
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(0, packed.size());
+
+ // Initial relocation.
+ AddRelocation(0xf00d0000, 10000, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(3, packed.size());
+ // One pair present.
+ EXPECT_EQ(1, packed[0]);
+ // Delta from the neutral element is the initial relocation.
+ EXPECT_EQ(0xf00d0000, packed[1]);
+ EXPECT_EQ(10000, packed[2]);
+
+ // Add a second relocation, 4 byte offset delta, 12 byte addend delta.
+ AddRelocation(0xf00d0004, 10012, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(5, packed.size());
+ // Two pairs present.
+ EXPECT_EQ(2, packed[0]);
+ // Delta from the neutral element is the initial relocation.
+ EXPECT_EQ(0xf00d0000, packed[1]);
+ EXPECT_EQ(10000, packed[2]);
+ // 4 byte offset delta, 12 byte addend delta.
+ EXPECT_EQ(4, packed[3]);
+ EXPECT_EQ(12, packed[4]);
+
+ // Add a third relocation, 4 byte offset delta, 12 byte addend delta.
+ AddRelocation(0xf00d0008, 10024, &relocations);
+
+ // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas.
+ AddRelocation(0xf00d0010, 10000, &relocations);
+ AddRelocation(0xf00d0018, 9976, &relocations);
+ AddRelocation(0xf00d0020, 9952, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(13, packed.size());
+ // Six pairs present.
+ EXPECT_EQ(6, packed[0]);
+ // Initial relocation.
+ EXPECT_EQ(0xf00d0000, packed[1]);
+ EXPECT_EQ(10000, packed[2]);
+ // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+ EXPECT_EQ(4, packed[3]);
+ EXPECT_EQ(12, packed[4]);
+ EXPECT_EQ(4, packed[5]);
+ EXPECT_EQ(12, packed[6]);
+ // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+ EXPECT_EQ(8, packed[7]);
+ EXPECT_EQ(-24, packed[8]);
+ EXPECT_EQ(8, packed[9]);
+ EXPECT_EQ(-24, packed[10]);
+ EXPECT_EQ(8, packed[11]);
+ EXPECT_EQ(-24, packed[12]);
+}
+
+TEST(Delta, Decode) {
+ std::vector<ELF::Sxword> packed;
+ std::vector<ELF::Rela> relocations;
+
+ RelocationDeltaCodec codec;
+ codec.Decode(packed, &relocations);
+
+ EXPECT_EQ(0, relocations.size());
+
+ // Six pairs.
+ packed.push_back(6);
+ // Initial relocation.
+ packed.push_back(0xc0de0000);
+ packed.push_back(10000);
+ // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+ packed.push_back(4);
+ packed.push_back(12);
+ packed.push_back(4);
+ packed.push_back(12);
+ // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+ packed.push_back(8);
+ packed.push_back(-24);
+ packed.push_back(8);
+ packed.push_back(-24);
+ packed.push_back(8);
+ packed.push_back(-24);
+
+ relocations.clear();
+ codec.Decode(packed, &relocations);
+
+ EXPECT_EQ(6, relocations.size());
+ // Initial relocation.
+ EXPECT_TRUE(CheckRelocation(0xc0de0000, 10000, relocations[0]));
+ // Two relocations, 4 byte offset deltas, 12 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation(0xc0de0004, 10012, relocations[1]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0008, 10024, relocations[2]));
+ // Three relocations, 8 byte offset deltas, -24 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation(0xc0de0010, 10000, relocations[3]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0018, 9976, relocations[4]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0020, 9952, relocations[5]));
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/elf_file.cc b/tools/relocation_packer/src/elf_file.cc
new file mode 100644
index 0000000..62c390a
--- /dev/null
+++ b/tools/relocation_packer/src/elf_file.cc
@@ -0,0 +1,1285 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation notes:
+//
+// We need to remove a piece from the ELF shared library. However, we also
+// want to ensure that code and data loads at the same addresses as before
+// packing, so that tools like breakpad can still match up addresses found
+// in any crash dumps with data extracted from the pre-packed version of
+// the shared library.
+//
+// Arranging this means that we have to split one of the LOAD segments into
+// two. Unfortunately, the program headers are located at the very start
+// of the shared library file, so expanding the program header section
+// would cause a lot of consequent changes to files offsets that we don't
+// really want to have to handle.
+//
+// Luckily, though, there is a segment that is always present and always
+// unused on Android; the GNU_STACK segment. What we do is to steal that
+// and repurpose it to be one of the split LOAD segments. We then have to
+// sort LOAD segments by offset to keep the crazy linker happy.
+//
+// All of this takes place in SplitProgramHeadersForHole(), used on packing,
+// and is unraveled on unpacking in CoalesceProgramHeadersForHole(). See
+// commentary on those functions for an example of this segment stealing
+// in action.
+
+#include "elf_file.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "debug.h"
+#include "elf_traits.h"
+#include "libelf.h"
+#include "packer.h"
+
+namespace relocation_packer {
+
+// Stub identifier written to 'null out' packed data, "NULL".
+static const uint32_t kStubIdentifier = 0x4c4c554eu;
+
+// Out-of-band dynamic tags used to indicate the offset and size of the
+// android packed relocations section.
+static const ELF::Sword DT_ANDROID_REL_OFFSET = DT_LOOS;
+static const ELF::Sword DT_ANDROID_REL_SIZE = DT_LOOS + 1;
+
+// Alignment to preserve, in bytes. This must be at least as large as the
+// largest d_align and sh_addralign values found in the loaded file.
+// Out of caution for RELRO page alignment, we preserve to a complete target
+// page. See http://www.airs.com/blog/archives/189.
+static const size_t kPreserveAlignment = 4096;
+
+namespace {
+
+// Get section data. Checks that the section has exactly one data entry,
+// so that the section size and the data size are the same. True in
+// practice for all sections we resize when packing or unpacking. Done
+// by ensuring that a call to elf_getdata(section, data) returns NULL as
+// the next data entry.
+Elf_Data* GetSectionData(Elf_Scn* section) {
+ Elf_Data* data = elf_getdata(section, NULL);
+ CHECK(data && elf_getdata(section, data) == NULL);
+ return data;
+}
+
+// Rewrite section data. Allocates new data and makes it the data element's
+// buffer. Relies on program exit to free allocated data.
+void RewriteSectionData(Elf_Data* data,
+ const void* section_data,
+ size_t size) {
+ CHECK(size == data->d_size);
+ uint8_t* area = new uint8_t[size];
+ memcpy(area, section_data, size);
+ data->d_buf = area;
+}
+
+// Verbose ELF header logging.
+void VerboseLogElfHeader(const ELF::Ehdr* elf_header) {
+ VLOG(1) << "e_phoff = " << elf_header->e_phoff;
+ VLOG(1) << "e_shoff = " << elf_header->e_shoff;
+ VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
+ VLOG(1) << "e_phentsize = " << elf_header->e_phentsize;
+ VLOG(1) << "e_phnum = " << elf_header->e_phnum;
+ VLOG(1) << "e_shnum = " << elf_header->e_shnum;
+ VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx;
+}
+
+// Verbose ELF program header logging.
+void VerboseLogProgramHeader(size_t program_header_index,
+ const ELF::Phdr* program_header) {
+ std::string type;
+ switch (program_header->p_type) {
+ case PT_NULL: type = "NULL"; break;
+ case PT_LOAD: type = "LOAD"; break;
+ case PT_DYNAMIC: type = "DYNAMIC"; break;
+ case PT_INTERP: type = "INTERP"; break;
+ case PT_PHDR: type = "PHDR"; break;
+ case PT_GNU_RELRO: type = "GNU_RELRO"; break;
+ case PT_GNU_STACK: type = "GNU_STACK"; break;
+ case PT_ARM_EXIDX: type = "EXIDX"; break;
+ default: type = "(OTHER)"; break;
+ }
+ VLOG(1) << "phdr[" << program_header_index << "] : " << type;
+ VLOG(1) << " p_offset = " << program_header->p_offset;
+ VLOG(1) << " p_vaddr = " << program_header->p_vaddr;
+ VLOG(1) << " p_paddr = " << program_header->p_paddr;
+ VLOG(1) << " p_filesz = " << program_header->p_filesz;
+ VLOG(1) << " p_memsz = " << program_header->p_memsz;
+ VLOG(1) << " p_flags = " << program_header->p_flags;
+ VLOG(1) << " p_align = " << program_header->p_align;
+}
+
+// Verbose ELF section header logging.
+void VerboseLogSectionHeader(const std::string& section_name,
+ const ELF::Shdr* section_header) {
+ VLOG(1) << "section " << section_name;
+ VLOG(1) << " sh_addr = " << section_header->sh_addr;
+ VLOG(1) << " sh_offset = " << section_header->sh_offset;
+ VLOG(1) << " sh_size = " << section_header->sh_size;
+ VLOG(1) << " sh_addralign = " << section_header->sh_addralign;
+}
+
+// Verbose ELF section data logging.
+void VerboseLogSectionData(const Elf_Data* data) {
+ VLOG(1) << " data";
+ VLOG(1) << " d_buf = " << data->d_buf;
+ VLOG(1) << " d_off = " << data->d_off;
+ VLOG(1) << " d_size = " << data->d_size;
+ VLOG(1) << " d_align = " << data->d_align;
+}
+
+} // namespace
+
+// Load the complete ELF file into a memory image in libelf, and identify
+// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or
+// .android.rela.dyn sections. No-op if the ELF file has already been loaded.
+bool ElfFile::Load() {
+ if (elf_)
+ return true;
+
+ Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL);
+ CHECK(elf);
+
+ if (elf_kind(elf) != ELF_K_ELF) {
+ LOG(ERROR) << "File not in ELF format";
+ return false;
+ }
+
+ ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ if (!elf_header) {
+ LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno());
+ return false;
+ }
+ if (elf_header->e_machine != ELF::kMachine) {
+ LOG(ERROR) << "ELF file architecture is not " << ELF::Machine();
+ return false;
+ }
+ if (elf_header->e_type != ET_DYN) {
+ LOG(ERROR) << "ELF file is not a shared object";
+ return false;
+ }
+
+ // Require that our endianness matches that of the target, and that both
+ // are little-endian. Safe for all current build/target combinations.
+ const int endian = elf_header->e_ident[EI_DATA];
+ CHECK(endian == ELFDATA2LSB);
+ CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
+
+ // Also require that the file class is as expected.
+ const int file_class = elf_header->e_ident[EI_CLASS];
+ CHECK(file_class == ELF::kFileClass);
+
+ VLOG(1) << "endian = " << endian << ", file class = " << file_class;
+ VerboseLogElfHeader(elf_header);
+
+ const ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header);
+
+ const ELF::Phdr* dynamic_program_header = NULL;
+ for (size_t i = 0; i < elf_header->e_phnum; ++i) {
+ const ELF::Phdr* program_header = &elf_program_header[i];
+ VerboseLogProgramHeader(i, program_header);
+
+ if (program_header->p_type == PT_DYNAMIC) {
+ CHECK(dynamic_program_header == NULL);
+ dynamic_program_header = program_header;
+ }
+ }
+ CHECK(dynamic_program_header != NULL);
+
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+
+ // Notes of the dynamic relocations, packed relocations, and .dynamic
+ // sections. Found while iterating sections, and later stored in class
+ // attributes.
+ Elf_Scn* found_relocations_section = NULL;
+ Elf_Scn* found_android_relocations_section = NULL;
+ Elf_Scn* found_dynamic_section = NULL;
+
+ // Notes of relocation section types seen. We require one or the other of
+ // these; both is unsupported.
+ bool has_rel_relocations = false;
+ bool has_rela_relocations = false;
+
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ const ELF::Shdr* section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+ VerboseLogSectionHeader(name, section_header);
+
+ // Note relocation section types.
+ if (section_header->sh_type == SHT_REL) {
+ has_rel_relocations = true;
+ }
+ if (section_header->sh_type == SHT_RELA) {
+ has_rela_relocations = true;
+ }
+
+ // Note special sections as we encounter them.
+ if ((name == ".rel.dyn" || name == ".rela.dyn") &&
+ section_header->sh_size > 0) {
+ found_relocations_section = section;
+ }
+ if ((name == ".android.rel.dyn" || name == ".android.rela.dyn") &&
+ section_header->sh_size > 0) {
+ found_android_relocations_section = section;
+ }
+ if (section_header->sh_offset == dynamic_program_header->p_offset) {
+ found_dynamic_section = section;
+ }
+
+ // Ensure we preserve alignment, repeated later for the data block(s).
+ CHECK(section_header->sh_addralign <= kPreserveAlignment);
+
+ Elf_Data* data = NULL;
+ while ((data = elf_getdata(section, data)) != NULL) {
+ CHECK(data->d_align <= kPreserveAlignment);
+ VerboseLogSectionData(data);
+ }
+ }
+
+ // Loading failed if we did not find the required special sections.
+ if (!found_relocations_section) {
+ LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section";
+ return false;
+ }
+ if (!found_android_relocations_section) {
+ LOG(ERROR) << "Missing or empty .android.rel.dyn or .android.rela.dyn "
+ << "section (to fix, run with --help and follow the "
+ << "pre-packing instructions)";
+ return false;
+ }
+ if (!found_dynamic_section) {
+ LOG(ERROR) << "Missing .dynamic section";
+ return false;
+ }
+
+ // Loading failed if we could not identify the relocations type.
+ if (!has_rel_relocations && !has_rela_relocations) {
+ LOG(ERROR) << "No relocations sections found";
+ return false;
+ }
+ if (has_rel_relocations && has_rela_relocations) {
+ LOG(ERROR) << "Multiple relocations sections with different types found, "
+ << "not currently supported";
+ return false;
+ }
+
+ elf_ = elf;
+ relocations_section_ = found_relocations_section;
+ dynamic_section_ = found_dynamic_section;
+ android_relocations_section_ = found_android_relocations_section;
+ relocations_type_ = has_rel_relocations ? REL : RELA;
+ return true;
+}
+
+namespace {
+
+// Helper for ResizeSection(). Adjust the main ELF header for the hole.
+void AdjustElfHeaderForHole(ELF::Ehdr* elf_header,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ if (elf_header->e_phoff > hole_start) {
+ elf_header->e_phoff += hole_size;
+ VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
+ }
+ if (elf_header->e_shoff > hole_start) {
+ elf_header->e_shoff += hole_size;
+ VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff;
+ }
+}
+
+// Helper for ResizeSection(). Adjust all section headers for the hole.
+void AdjustSectionHeadersForHole(Elf* elf,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ ELF::Shdr* section_header = ELF::getshdr(section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+
+ if (section_header->sh_offset > hole_start) {
+ section_header->sh_offset += hole_size;
+ VLOG(1) << "section " << name
+ << " sh_offset adjusted to " << section_header->sh_offset;
+ }
+ }
+}
+
+// Helper for ResizeSection(). Adjust the offsets of any program headers
+// that have offsets currently beyond the hole start.
+void AdjustProgramHeaderOffsets(ELF::Phdr* program_headers,
+ size_t count,
+ ELF::Phdr* ignored_1,
+ ELF::Phdr* ignored_2,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ for (size_t i = 0; i < count; ++i) {
+ ELF::Phdr* program_header = &program_headers[i];
+
+ if (program_header == ignored_1 || program_header == ignored_2)
+ continue;
+
+ if (program_header->p_offset > hole_start) {
+ // The hole start is past this segment, so adjust offset.
+ program_header->p_offset += hole_size;
+ VLOG(1) << "phdr[" << i
+ << "] p_offset adjusted to "<< program_header->p_offset;
+ }
+ }
+}
+
+// Helper for ResizeSection(). Find the first loadable segment in the
+// file. We expect it to map from file offset zero.
+ELF::Phdr* FindFirstLoadSegment(ELF::Phdr* program_headers,
+ size_t count) {
+ ELF::Phdr* first_loadable_segment = NULL;
+
+ for (size_t i = 0; i < count; ++i) {
+ ELF::Phdr* program_header = &program_headers[i];
+
+ if (program_header->p_type == PT_LOAD &&
+ program_header->p_offset == 0 &&
+ program_header->p_vaddr == 0 &&
+ program_header->p_paddr == 0) {
+ first_loadable_segment = program_header;
+ }
+ }
+ LOG_IF(FATAL, !first_loadable_segment)
+ << "Cannot locate a LOAD segment with address and offset zero";
+
+ return first_loadable_segment;
+}
+
+// Helper for ResizeSection(). Find the PT_GNU_STACK segment, and check
+// that it contains what we expect so we can restore it on unpack if needed.
+ELF::Phdr* FindUnusedGnuStackSegment(ELF::Phdr* program_headers,
+ size_t count) {
+ ELF::Phdr* unused_segment = NULL;
+
+ for (size_t i = 0; i < count; ++i) {
+ ELF::Phdr* program_header = &program_headers[i];
+
+ if (program_header->p_type == PT_GNU_STACK &&
+ program_header->p_offset == 0 &&
+ program_header->p_vaddr == 0 &&
+ program_header->p_paddr == 0 &&
+ program_header->p_filesz == 0 &&
+ program_header->p_memsz == 0 &&
+ program_header->p_flags == (PF_R | PF_W) &&
+ program_header->p_align == ELF::kGnuStackSegmentAlignment) {
+ unused_segment = program_header;
+ }
+ }
+ LOG_IF(FATAL, !unused_segment)
+ << "Cannot locate the expected GNU_STACK segment";
+
+ return unused_segment;
+}
+
+// Helper for ResizeSection(). Find the segment that was the first loadable
+// one before we split it into two. This is the one into which we coalesce
+// the split segments on unpacking.
+ELF::Phdr* FindOriginalFirstLoadSegment(ELF::Phdr* program_headers,
+ size_t count) {
+ const ELF::Phdr* first_loadable_segment =
+ FindFirstLoadSegment(program_headers, count);
+
+ ELF::Phdr* original_first_loadable_segment = NULL;
+
+ for (size_t i = 0; i < count; ++i) {
+ ELF::Phdr* program_header = &program_headers[i];
+
+ // The original first loadable segment is the one that follows on from
+ // the one we wrote on split to be the current first loadable segment.
+ if (program_header->p_type == PT_LOAD &&
+ program_header->p_offset == first_loadable_segment->p_filesz) {
+ original_first_loadable_segment = program_header;
+ }
+ }
+ LOG_IF(FATAL, !original_first_loadable_segment)
+ << "Cannot locate the LOAD segment that follows a LOAD at offset zero";
+
+ return original_first_loadable_segment;
+}
+
+// Helper for ResizeSection(). Find the segment that contains the hole.
+Elf_Scn* FindSectionContainingHole(Elf* elf,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ Elf_Scn* section = NULL;
+ Elf_Scn* last_unholed_section = NULL;
+
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ const ELF::Shdr* section_header = ELF::getshdr(section);
+
+ // Because we get here after section headers have been adjusted for the
+ // hole, we need to 'undo' that adjustment to give a view of the original
+ // sections layout.
+ ELF::Off offset = section_header->sh_offset;
+ if (section_header->sh_offset >= hole_start) {
+ offset -= hole_size;
+ }
+
+ if (offset <= hole_start) {
+ last_unholed_section = section;
+ }
+ }
+ LOG_IF(FATAL, !last_unholed_section)
+ << "Cannot identify the section before the one containing the hole";
+
+ // The section containing the hole is the one after the last one found
+ // by the loop above.
+ Elf_Scn* holed_section = elf_nextscn(elf, last_unholed_section);
+ LOG_IF(FATAL, !holed_section)
+ << "Cannot identify the section containing the hole";
+
+ return holed_section;
+}
+
+// Helper for ResizeSection(). Find the last section contained in a segment.
+Elf_Scn* FindLastSectionInSegment(Elf* elf,
+ ELF::Phdr* program_header,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ const ELF::Off segment_end =
+ program_header->p_offset + program_header->p_filesz;
+
+ Elf_Scn* section = NULL;
+ Elf_Scn* last_section = NULL;
+
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ const ELF::Shdr* section_header = ELF::getshdr(section);
+
+ // As above, 'undo' any section offset adjustment to give a view of the
+ // original sections layout.
+ ELF::Off offset = section_header->sh_offset;
+ if (section_header->sh_offset >= hole_start) {
+ offset -= hole_size;
+ }
+
+ if (offset < segment_end) {
+ last_section = section;
+ }
+ }
+ LOG_IF(FATAL, !last_section)
+ << "Cannot identify the last section in the given segment";
+
+ return last_section;
+}
+
+// Helper for ResizeSection(). Order loadable segments by their offsets.
+// The crazy linker contains assumptions about loadable segment ordering,
+// and it is better if we do not break them.
+void SortOrderSensitiveProgramHeaders(ELF::Phdr* program_headers,
+ size_t count) {
+ std::vector<ELF::Phdr*> orderable;
+
+ // Collect together orderable program headers. These are all the LOAD
+ // segments, and any GNU_STACK that may be present (removed on packing,
+ // but replaced on unpacking).
+ for (size_t i = 0; i < count; ++i) {
+ ELF::Phdr* program_header = &program_headers[i];
+
+ if (program_header->p_type == PT_LOAD ||
+ program_header->p_type == PT_GNU_STACK) {
+ orderable.push_back(program_header);
+ }
+ }
+
+ // Order these program headers so that any PT_GNU_STACK is last, and
+ // the LOAD segments that precede it appear in offset order. Uses
+ // insertion sort.
+ for (size_t i = 1; i < orderable.size(); ++i) {
+ for (size_t j = i; j > 0; --j) {
+ ELF::Phdr* first = orderable[j - 1];
+ ELF::Phdr* second = orderable[j];
+
+ if (!(first->p_type == PT_GNU_STACK ||
+ first->p_offset > second->p_offset)) {
+ break;
+ }
+ std::swap(*first, *second);
+ }
+ }
+}
+
+// Helper for ResizeSection(). The GNU_STACK program header is unused in
+// Android, so we can repurpose it here. Before packing, the program header
+// table contains something like:
+//
+// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
+// LOAD 0x000000 0x00000000 0x00000000 0x1efc818 0x1efc818 R E 0x1000
+// LOAD 0x1efd008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000
+// DYNAMIC 0x205ec50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4
+// GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
+//
+// The hole in the file is in the first of these. In order to preserve all
+// load addresses, what we do is to turn the GNU_STACK into a new LOAD entry
+// that maps segments up to where we created the hole, adjust the first LOAD
+// entry so that it maps segments after that, adjust any other program
+// headers whose offset is after the hole start, and finally order the LOAD
+// segments by offset, to give:
+//
+// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
+// LOAD 0x000000 0x00000000 0x00000000 0x14ea4 0x14ea4 R E 0x1000
+// LOAD 0x014ea4 0x00212ea4 0x00212ea4 0x1cea164 0x1cea164 R E 0x1000
+// DYNAMIC 0x1e60c50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4
+// LOAD 0x1cff008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000
+//
+// We work out the split points by finding the .rel.dyn or .rela.dyn section
+// that contains the hole, and by finding the last section in a given segment.
+//
+// To unpack, we reverse the above to leave the file as it was originally.
+void SplitProgramHeadersForHole(Elf* elf,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ CHECK(hole_size < 0);
+ const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ CHECK(elf_header);
+
+ ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header);
+
+ const size_t program_header_count = elf_header->e_phnum;
+
+ // Locate the segment that we can overwrite to form the new LOAD entry,
+ // and the segment that we are going to split into two parts.
+ ELF::Phdr* spliced_header =
+ FindUnusedGnuStackSegment(elf_program_header, program_header_count);
+ ELF::Phdr* split_header =
+ FindFirstLoadSegment(elf_program_header, program_header_count);
+
+ VLOG(1) << "phdr[" << split_header - elf_program_header << "] split";
+ VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] new LOAD";
+
+ // Find the section that contains the hole. We split on the section that
+ // follows it.
+ Elf_Scn* holed_section =
+ FindSectionContainingHole(elf, hole_start, hole_size);
+
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+
+ ELF::Shdr* section_header = ELF::getshdr(holed_section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+ VLOG(1) << "section " << name << " split after";
+
+ // Find the last section in the segment we are splitting.
+ Elf_Scn* last_section =
+ FindLastSectionInSegment(elf, split_header, hole_start, hole_size);
+
+ section_header = ELF::getshdr(last_section);
+ name = elf_strptr(elf, string_index, section_header->sh_name);
+ VLOG(1) << "section " << name << " split end";
+
+ // Split on the section following the holed one, and up to (but not
+ // including) the section following the last one in the split segment.
+ Elf_Scn* split_section = elf_nextscn(elf, holed_section);
+ LOG_IF(FATAL, !split_section)
+ << "No section follows the section that contains the hole";
+ Elf_Scn* end_section = elf_nextscn(elf, last_section);
+ LOG_IF(FATAL, !end_section)
+ << "No section follows the last section in the segment being split";
+
+ // Split the first portion of split_header into spliced_header.
+ const ELF::Shdr* split_section_header = ELF::getshdr(split_section);
+ spliced_header->p_type = split_header->p_type;
+ spliced_header->p_offset = split_header->p_offset;
+ spliced_header->p_vaddr = split_header->p_vaddr;
+ spliced_header->p_paddr = split_header->p_paddr;
+ CHECK(split_header->p_filesz == split_header->p_memsz);
+ spliced_header->p_filesz = split_section_header->sh_offset;
+ spliced_header->p_memsz = split_section_header->sh_offset;
+ spliced_header->p_flags = split_header->p_flags;
+ spliced_header->p_align = split_header->p_align;
+
+ // Now rewrite split_header to remove the part we spliced from it.
+ const ELF::Shdr* end_section_header = ELF::getshdr(end_section);
+ split_header->p_offset = spliced_header->p_filesz;
+ CHECK(split_header->p_vaddr == split_header->p_paddr);
+ split_header->p_vaddr = split_section_header->sh_addr;
+ split_header->p_paddr = split_section_header->sh_addr;
+ CHECK(split_header->p_filesz == split_header->p_memsz);
+ split_header->p_filesz =
+ end_section_header->sh_offset - spliced_header->p_filesz;
+ split_header->p_memsz =
+ end_section_header->sh_offset - spliced_header->p_filesz;
+
+ // Adjust the offsets of all program headers that are not one of the pair
+ // we just created by splitting.
+ AdjustProgramHeaderOffsets(elf_program_header,
+ program_header_count,
+ spliced_header,
+ split_header,
+ hole_start,
+ hole_size);
+
+ // Finally, order loadable segments by offset/address. The crazy linker
+ // contains assumptions about loadable segment ordering.
+ SortOrderSensitiveProgramHeaders(elf_program_header,
+ program_header_count);
+}
+
+// Helper for ResizeSection(). Undo the work of SplitProgramHeadersForHole().
+void CoalesceProgramHeadersForHole(Elf* elf,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ CHECK(hole_size > 0);
+ const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ CHECK(elf_header);
+
+ ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header);
+
+ const size_t program_header_count = elf_header->e_phnum;
+
+ // Locate the segment that we overwrote to form the new LOAD entry, and
+ // the segment that we split into two parts on packing.
+ ELF::Phdr* spliced_header =
+ FindFirstLoadSegment(elf_program_header, program_header_count);
+ ELF::Phdr* split_header =
+ FindOriginalFirstLoadSegment(elf_program_header, program_header_count);
+
+ VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] stack";
+ VLOG(1) << "phdr[" << split_header - elf_program_header << "] coalesce";
+
+ // Find the last section in the second segment we are coalescing.
+ Elf_Scn* last_section =
+ FindLastSectionInSegment(elf, split_header, hole_start, hole_size);
+
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+
+ const ELF::Shdr* section_header = ELF::getshdr(last_section);
+ std::string name = elf_strptr(elf, string_index, section_header->sh_name);
+ VLOG(1) << "section " << name << " coalesced";
+
+ // Rewrite the coalesced segment into split_header.
+ const ELF::Shdr* last_section_header = ELF::getshdr(last_section);
+ split_header->p_offset = spliced_header->p_offset;
+ CHECK(split_header->p_vaddr == split_header->p_paddr);
+ split_header->p_vaddr = spliced_header->p_vaddr;
+ split_header->p_paddr = spliced_header->p_vaddr;
+ CHECK(split_header->p_filesz == split_header->p_memsz);
+ split_header->p_filesz =
+ last_section_header->sh_offset + last_section_header->sh_size;
+ split_header->p_memsz =
+ last_section_header->sh_offset + last_section_header->sh_size;
+
+ // Reconstruct the original GNU_STACK segment into spliced_header.
+ spliced_header->p_type = PT_GNU_STACK;
+ spliced_header->p_offset = 0;
+ spliced_header->p_vaddr = 0;
+ spliced_header->p_paddr = 0;
+ spliced_header->p_filesz = 0;
+ spliced_header->p_memsz = 0;
+ spliced_header->p_flags = PF_R | PF_W;
+ spliced_header->p_align = ELF::kGnuStackSegmentAlignment;
+
+ // Adjust the offsets of all program headers that are not one of the pair
+ // we just coalesced.
+ AdjustProgramHeaderOffsets(elf_program_header,
+ program_header_count,
+ spliced_header,
+ split_header,
+ hole_start,
+ hole_size);
+
+ // Finally, order loadable segments by offset/address. The crazy linker
+ // contains assumptions about loadable segment ordering.
+ SortOrderSensitiveProgramHeaders(elf_program_header,
+ program_header_count);
+}
+
+// Helper for ResizeSection(). Rewrite program headers.
+void RewriteProgramHeadersForHole(Elf* elf,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ // If hole_size is negative then we are removing a piece of the file, and
+ // we want to split program headers so that we keep the same addresses
+ // for text and data. If positive, then we are putting that piece of the
+ // file back in, so we coalesce the previously split program headers.
+ if (hole_size < 0)
+ SplitProgramHeadersForHole(elf, hole_start, hole_size);
+ else if (hole_size > 0)
+ CoalesceProgramHeadersForHole(elf, hole_start, hole_size);
+}
+
+// Helper for ResizeSection(). Locate and return the dynamic section.
+Elf_Scn* GetDynamicSection(Elf* elf) {
+ const ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ CHECK(elf_header);
+
+ const ELF::Phdr* elf_program_header = ELF::getphdr(elf);
+ CHECK(elf_program_header);
+
+ // Find the program header that describes the dynamic section.
+ const ELF::Phdr* dynamic_program_header = NULL;
+ for (size_t i = 0; i < elf_header->e_phnum; ++i) {
+ const ELF::Phdr* program_header = &elf_program_header[i];
+
+ if (program_header->p_type == PT_DYNAMIC) {
+ dynamic_program_header = program_header;
+ }
+ }
+ CHECK(dynamic_program_header);
+
+ // Now find the section with the same offset as this program header.
+ Elf_Scn* dynamic_section = NULL;
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ ELF::Shdr* section_header = ELF::getshdr(section);
+
+ if (section_header->sh_offset == dynamic_program_header->p_offset) {
+ dynamic_section = section;
+ }
+ }
+ CHECK(dynamic_section != NULL);
+
+ return dynamic_section;
+}
+
+// Helper for ResizeSection(). Adjust the .dynamic section for the hole.
+template <typename Rel>
+void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
+ ELF::Off hole_start,
+ ssize_t hole_size) {
+ Elf_Data* data = GetSectionData(dynamic_section);
+
+ const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
+ std::vector<ELF::Dyn> dynamics(
+ dynamic_base,
+ dynamic_base + data->d_size / sizeof(dynamics[0]));
+
+ for (size_t i = 0; i < dynamics.size(); ++i) {
+ ELF::Dyn* dynamic = &dynamics[i];
+ const ELF::Sword tag = dynamic->d_tag;
+
+ // DT_RELSZ or DT_RELASZ indicate the overall size of relocations.
+ // Only one will be present. Adjust by hole size.
+ if (tag == DT_RELSZ || tag == DT_RELASZ) {
+ dynamic->d_un.d_val += hole_size;
+ VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+ << " d_val adjusted to " << dynamic->d_un.d_val;
+ }
+
+ // DT_RELCOUNT or DT_RELACOUNT hold the count of relative relocations.
+ // Only one will be present. Packing reduces it to the alignment
+ // padding, if any; unpacking restores it to its former value. The
+ // crazy linker does not use it, but we update it anyway.
+ if (tag == DT_RELCOUNT || tag == DT_RELACOUNT) {
+ // Cast sizeof to a signed type to avoid the division result being
+ // promoted into an unsigned size_t.
+ const ssize_t sizeof_rel = static_cast<ssize_t>(sizeof(Rel));
+ dynamic->d_un.d_val += hole_size / sizeof_rel;
+ VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
+ << " d_val adjusted to " << dynamic->d_un.d_val;
+ }
+
+ // DT_RELENT and DT_RELAENT do not change, but make sure they are what
+ // we expect. Only one will be present.
+ if (tag == DT_RELENT || tag == DT_RELAENT) {
+ CHECK(dynamic->d_un.d_val == sizeof(Rel));
+ }
+ }
+
+ void* section_data = &dynamics[0];
+ size_t bytes = dynamics.size() * sizeof(dynamics[0]);
+ RewriteSectionData(data, section_data, bytes);
+}
+
+// Resize a section. If the new size is larger than the current size, open
+// up a hole by increasing file offsets that come after the hole. If smaller
+// than the current size, remove the hole by decreasing those offsets.
+template <typename Rel>
+void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) {
+ ELF::Shdr* section_header = ELF::getshdr(section);
+ if (section_header->sh_size == new_size)
+ return;
+
+ // Note if we are resizing the real dyn relocations.
+ size_t string_index;
+ elf_getshdrstrndx(elf, &string_index);
+ const std::string section_name =
+ elf_strptr(elf, string_index, section_header->sh_name);
+ const bool is_relocations_resize =
+ (section_name == ".rel.dyn" || section_name == ".rela.dyn");
+
+ // Require that the section size and the data size are the same. True
+ // in practice for all sections we resize when packing or unpacking.
+ Elf_Data* data = GetSectionData(section);
+ CHECK(data->d_off == 0 && data->d_size == section_header->sh_size);
+
+ // Require that the section is not zero-length (that is, has allocated
+ // data that we can validly expand).
+ CHECK(data->d_size && data->d_buf);
+
+ const ELF::Off hole_start = section_header->sh_offset;
+ const ssize_t hole_size = new_size - data->d_size;
+
+ VLOG_IF(1, (hole_size > 0)) << "expand section size = " << data->d_size;
+ VLOG_IF(1, (hole_size < 0)) << "shrink section size = " << data->d_size;
+
+ // Resize the data and the section header.
+ data->d_size += hole_size;
+ section_header->sh_size += hole_size;
+
+ // Add the hole size to all offsets in the ELF file that are after the
+ // start of the hole. If the hole size is positive we are expanding the
+ // section to create a new hole; if negative, we are closing up a hole.
+
+ // Start with the main ELF header.
+ ELF::Ehdr* elf_header = ELF::getehdr(elf);
+ AdjustElfHeaderForHole(elf_header, hole_start, hole_size);
+
+ // Adjust all section headers.
+ AdjustSectionHeadersForHole(elf, hole_start, hole_size);
+
+ // If resizing the dynamic relocations, rewrite the program headers to
+ // either split or coalesce segments, and adjust dynamic entries to match.
+ if (is_relocations_resize) {
+ RewriteProgramHeadersForHole(elf, hole_start, hole_size);
+
+ Elf_Scn* dynamic_section = GetDynamicSection(elf);
+ AdjustDynamicSectionForHole<Rel>(dynamic_section, hole_start, hole_size);
+ }
+}
+
+// Find the first slot in a dynamics array with the given tag. The array
+// always ends with a free (unused) element, and which we exclude from the
+// search. Returns dynamics->size() if not found.
+size_t FindDynamicEntry(ELF::Sword tag,
+ std::vector<ELF::Dyn>* dynamics) {
+ // Loop until the penultimate entry. We exclude the end sentinel.
+ for (size_t i = 0; i < dynamics->size() - 1; ++i) {
+ if (dynamics->at(i).d_tag == tag)
+ return i;
+ }
+
+ // The tag was not found.
+ return dynamics->size();
+}
+
+// Replace the first free (unused) slot in a dynamics vector with the given
+// value. The vector always ends with a free (unused) element, so the slot
+// found cannot be the last one in the vector.
+void AddDynamicEntry(const ELF::Dyn& dyn,
+ std::vector<ELF::Dyn>* dynamics) {
+ const size_t slot = FindDynamicEntry(DT_NULL, dynamics);
+ if (slot == dynamics->size()) {
+ LOG(FATAL) << "No spare dynamic array slots found "
+ << "(to fix, increase gold's --spare-dynamic-tags value)";
+ }
+
+ // Replace this entry with the one supplied.
+ dynamics->at(slot) = dyn;
+ VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag;
+}
+
+// Remove the element in the dynamics vector that matches the given tag with
+// unused slot data. Shuffle the following elements up, and ensure that the
+// last is the null sentinel.
+void RemoveDynamicEntry(ELF::Sword tag,
+ std::vector<ELF::Dyn>* dynamics) {
+ const size_t slot = FindDynamicEntry(tag, dynamics);
+ CHECK(slot != dynamics->size());
+
+ // Remove this entry by shuffling up everything that follows.
+ for (size_t i = slot; i < dynamics->size() - 1; ++i) {
+ dynamics->at(i) = dynamics->at(i + 1);
+ VLOG(1) << "dynamic[" << i
+ << "] overwritten with dynamic[" << i + 1 << "]";
+ }
+
+ // Ensure that the end sentinel is still present.
+ CHECK(dynamics->at(dynamics->size() - 1).d_tag == DT_NULL);
+}
+
+// Construct a null relocation without addend.
+void NullRelocation(ELF::Rel* relocation) {
+ relocation->r_offset = 0;
+ relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
+}
+
+// Construct a null relocation with addend.
+void NullRelocation(ELF::Rela* relocation) {
+ relocation->r_offset = 0;
+ relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode);
+ relocation->r_addend = 0;
+}
+
+// Pad relocations with the given number of null entries. Generates its
+// null entry with the appropriate NullRelocation() invocation.
+template <typename Rel>
+void PadRelocations(size_t count, std::vector<Rel>* relocations) {
+ Rel null_relocation;
+ NullRelocation(&null_relocation);
+ std::vector<Rel> padding(count, null_relocation);
+ relocations->insert(relocations->end(), padding.begin(), padding.end());
+}
+
+} // namespace
+
+// Remove relative entries from dynamic relocations and write as packed
+// data into android packed relocations.
+bool ElfFile::PackRelocations() {
+ // Load the ELF file into libelf.
+ if (!Load()) {
+ LOG(ERROR) << "Failed to load as ELF";
+ return false;
+ }
+
+ // Retrieve the current dynamic relocations section data.
+ Elf_Data* data = GetSectionData(relocations_section_);
+
+ if (relocations_type_ == REL) {
+ // Convert data to a vector of relocations.
+ const ELF::Rel* relocations_base = reinterpret_cast<ELF::Rel*>(data->d_buf);
+ std::vector<ELF::Rel> relocations(
+ relocations_base,
+ relocations_base + data->d_size / sizeof(relocations[0]));
+
+ LOG(INFO) << "Relocations : REL";
+ return PackTypedRelocations<ELF::Rel>(relocations, data);
+ }
+
+ if (relocations_type_ == RELA) {
+ // Convert data to a vector of relocations with addends.
+ const ELF::Rela* relocations_base =
+ reinterpret_cast<ELF::Rela*>(data->d_buf);
+ std::vector<ELF::Rela> relocations(
+ relocations_base,
+ relocations_base + data->d_size / sizeof(relocations[0]));
+
+ LOG(INFO) << "Relocations : RELA";
+ return PackTypedRelocations<ELF::Rela>(relocations, data);
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
+template <typename Rel>
+bool ElfFile::PackTypedRelocations(const std::vector<Rel>& relocations,
+ Elf_Data* data) {
+ // Filter relocations into those that are relative and others.
+ std::vector<Rel> relative_relocations;
+ std::vector<Rel> other_relocations;
+
+ for (size_t i = 0; i < relocations.size(); ++i) {
+ const Rel& relocation = relocations[i];
+ if (ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode) {
+ CHECK(ELF_R_SYM(relocation.r_info) == 0);
+ relative_relocations.push_back(relocation);
+ } else {
+ other_relocations.push_back(relocation);
+ }
+ }
+ LOG(INFO) << "Relative : " << relative_relocations.size() << " entries";
+ LOG(INFO) << "Other : " << other_relocations.size() << " entries";
+ LOG(INFO) << "Total : " << relocations.size() << " entries";
+
+ // If no relative relocations then we have nothing packable. Perhaps
+ // the shared object has already been packed?
+ if (relative_relocations.empty()) {
+ LOG(ERROR) << "No relative relocations found (already packed?)";
+ return false;
+ }
+
+ // If not padding fully, apply only enough padding to preserve alignment.
+ // Otherwise, pad so that we do not shrink the relocations section at all.
+ if (!is_padding_relocations_) {
+ // Calculate the size of the hole we will close up when we rewrite
+ // dynamic relocations.
+ ssize_t hole_size =
+ relative_relocations.size() * sizeof(relative_relocations[0]);
+ const ssize_t unaligned_hole_size = hole_size;
+
+ // Adjust the actual hole size to preserve alignment. We always adjust
+ // by a whole number of NONE-type relocations.
+ while (hole_size % kPreserveAlignment)
+ hole_size -= sizeof(relative_relocations[0]);
+ LOG(INFO) << "Compaction : " << hole_size << " bytes";
+
+ // Adjusting for alignment may have removed any packing benefit.
+ if (hole_size == 0) {
+ LOG(INFO) << "Too few relative relocations to pack after alignment";
+ return false;
+ }
+
+ // Find the padding needed in other_relocations to preserve alignment.
+ // Ensure that we never completely empty the real relocations section.
+ size_t padding_bytes = unaligned_hole_size - hole_size;
+ if (padding_bytes == 0 && other_relocations.size() == 0) {
+ do {
+ padding_bytes += sizeof(relative_relocations[0]);
+ } while (padding_bytes % kPreserveAlignment);
+ }
+ CHECK(padding_bytes % sizeof(other_relocations[0]) == 0);
+ const size_t padding = padding_bytes / sizeof(other_relocations[0]);
+
+ // Padding may have removed any packing benefit.
+ if (padding >= relative_relocations.size()) {
+ LOG(INFO) << "Too few relative relocations to pack after padding";
+ return false;
+ }
+
+ // Add null relocations to other_relocations to preserve alignment.
+ PadRelocations<Rel>(padding, &other_relocations);
+ LOG(INFO) << "Alignment pad : " << padding << " relocations";
+ } else {
+ // If padding, add NONE-type relocations to other_relocations to make it
+ // the same size as the the original relocations we read in. This makes
+ // the ResizeSection() below a no-op.
+ const size_t padding = relocations.size() - other_relocations.size();
+ PadRelocations<Rel>(padding, &other_relocations);
+ }
+
+ // Pack relative relocations.
+ const size_t initial_bytes =
+ relative_relocations.size() * sizeof(relative_relocations[0]);
+ LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes";
+ std::vector<uint8_t> packed;
+ RelocationPacker packer;
+ packer.PackRelativeRelocations(relative_relocations, &packed);
+ const void* packed_data = &packed[0];
+ const size_t packed_bytes = packed.size() * sizeof(packed[0]);
+ LOG(INFO) << "Packed relative: " << packed_bytes << " bytes";
+
+ // If we have insufficient relative relocations to form a run then
+ // packing fails.
+ if (packed.empty()) {
+ LOG(INFO) << "Too few relative relocations to pack";
+ return false;
+ }
+
+ // Run a loopback self-test as a check that packing is lossless.
+ std::vector<Rel> unpacked;
+ packer.UnpackRelativeRelocations(packed, &unpacked);
+ CHECK(unpacked.size() == relative_relocations.size());
+ CHECK(!memcmp(&unpacked[0],
+ &relative_relocations[0],
+ unpacked.size() * sizeof(unpacked[0])));
+
+ // Make sure packing saved some space.
+ if (packed_bytes >= initial_bytes) {
+ LOG(INFO) << "Packing relative relocations saves no space";
+ return false;
+ }
+
+ // Rewrite the current dynamic relocations section to be only the ARM
+ // non-relative relocations, then shrink it to size.
+ const void* section_data = &other_relocations[0];
+ const size_t bytes = other_relocations.size() * sizeof(other_relocations[0]);
+ ResizeSection<Rel>(elf_, relocations_section_, bytes);
+ RewriteSectionData(data, section_data, bytes);
+
+ // Rewrite the current packed android relocations section to hold the packed
+ // relative relocations.
+ data = GetSectionData(android_relocations_section_);
+ ResizeSection<Rel>(elf_, android_relocations_section_, packed_bytes);
+ RewriteSectionData(data, packed_data, packed_bytes);
+
+ // Rewrite .dynamic to include two new tags describing the packed android
+ // relocations.
+ data = GetSectionData(dynamic_section_);
+ const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
+ std::vector<ELF::Dyn> dynamics(
+ dynamic_base,
+ dynamic_base + data->d_size / sizeof(dynamics[0]));
+ // Use two of the spare slots to describe the packed section.
+ ELF::Shdr* section_header = ELF::getshdr(android_relocations_section_);
+ {
+ ELF::Dyn dyn;
+ dyn.d_tag = DT_ANDROID_REL_OFFSET;
+ dyn.d_un.d_ptr = section_header->sh_offset;
+ AddDynamicEntry(dyn, &dynamics);
+ }
+ {
+ ELF::Dyn dyn;
+ dyn.d_tag = DT_ANDROID_REL_SIZE;
+ dyn.d_un.d_val = section_header->sh_size;
+ AddDynamicEntry(dyn, &dynamics);
+ }
+ const void* dynamics_data = &dynamics[0];
+ const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
+ RewriteSectionData(data, dynamics_data, dynamics_bytes);
+
+ Flush();
+ return true;
+}
+
+// Find packed relative relocations in the packed android relocations
+// section, unpack them, and rewrite the dynamic relocations section to
+// contain unpacked data.
+bool ElfFile::UnpackRelocations() {
+ // Load the ELF file into libelf.
+ if (!Load()) {
+ LOG(ERROR) << "Failed to load as ELF";
+ return false;
+ }
+
+ // Retrieve the current packed android relocations section data.
+ Elf_Data* data = GetSectionData(android_relocations_section_);
+
+ // Convert data to a vector of bytes.
+ const uint8_t* packed_base = reinterpret_cast<uint8_t*>(data->d_buf);
+ std::vector<uint8_t> packed(
+ packed_base,
+ packed_base + data->d_size / sizeof(packed[0]));
+
+ if (packed.size() > 3 &&
+ packed[0] == 'A' &&
+ packed[1] == 'P' &&
+ packed[2] == 'R' &&
+ packed[3] == '1') {
+ // Signature is APR1, unpack relocations.
+ CHECK(relocations_type_ == REL);
+ LOG(INFO) << "Relocations : REL";
+ return UnpackTypedRelocations<ELF::Rel>(packed, data);
+ }
+
+ if (packed.size() > 3 &&
+ packed[0] == 'A' &&
+ packed[1] == 'P' &&
+ packed[2] == 'A' &&
+ packed[3] == '1') {
+ // Signature is APA1, unpack relocations with addends.
+ CHECK(relocations_type_ == RELA);
+ LOG(INFO) << "Relocations : RELA";
+ return UnpackTypedRelocations<ELF::Rela>(packed, data);
+ }
+
+ LOG(ERROR) << "Packed relative relocations not found (not packed?)";
+ return false;
+}
+
+// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela.
+template <typename Rel>
+bool ElfFile::UnpackTypedRelocations(const std::vector<uint8_t>& packed,
+ Elf_Data* data) {
+ // Unpack the data to re-materialize the relative relocations.
+ const size_t packed_bytes = packed.size() * sizeof(packed[0]);
+ LOG(INFO) << "Packed relative: " << packed_bytes << " bytes";
+ std::vector<Rel> relative_relocations;
+ RelocationPacker packer;
+ packer.UnpackRelativeRelocations(packed, &relative_relocations);
+ const size_t unpacked_bytes =
+ relative_relocations.size() * sizeof(relative_relocations[0]);
+ LOG(INFO) << "Unpacked relative: " << unpacked_bytes << " bytes";
+
+ // Retrieve the current dynamic relocations section data.
+ data = GetSectionData(relocations_section_);
+
+ // Interpret data as relocations.
+ const Rel* relocations_base = reinterpret_cast<Rel*>(data->d_buf);
+ std::vector<Rel> relocations(
+ relocations_base,
+ relocations_base + data->d_size / sizeof(relocations[0]));
+
+ std::vector<Rel> other_relocations;
+ size_t padding = 0;
+
+ // Filter relocations to locate any that are NONE-type. These will occur
+ // if padding was turned on for packing.
+ for (size_t i = 0; i < relocations.size(); ++i) {
+ const Rel& relocation = relocations[i];
+ if (ELF_R_TYPE(relocation.r_info) != ELF::kNoRelocationCode) {
+ other_relocations.push_back(relocation);
+ } else {
+ ++padding;
+ }
+ }
+ LOG(INFO) << "Relative : " << relative_relocations.size() << " entries";
+ LOG(INFO) << "Other : " << other_relocations.size() << " entries";
+
+ // If we found the same number of null relocation entries in the dynamic
+ // relocations section as we hold as unpacked relative relocations, then
+ // this is a padded file.
+ const bool is_padded = padding == relative_relocations.size();
+
+ // Unless padded, report by how much we expand the file.
+ if (!is_padded) {
+ // Calculate the size of the hole we will open up when we rewrite
+ // dynamic relocations.
+ ssize_t hole_size =
+ relative_relocations.size() * sizeof(relative_relocations[0]);
+
+ // Adjust the hole size for the padding added to preserve alignment.
+ hole_size -= padding * sizeof(other_relocations[0]);
+ LOG(INFO) << "Expansion : " << hole_size << " bytes";
+ }
+
+ // Rewrite the current dynamic relocations section to be the relative
+ // relocations followed by other relocations. This is the usual order in
+ // which we find them after linking, so this action will normally put the
+ // entire dynamic relocations section back to its pre-split-and-packed state.
+ relocations.assign(relative_relocations.begin(), relative_relocations.end());
+ relocations.insert(relocations.end(),
+ other_relocations.begin(), other_relocations.end());
+ const void* section_data = &relocations[0];
+ const size_t bytes = relocations.size() * sizeof(relocations[0]);
+ LOG(INFO) << "Total : " << relocations.size() << " entries";
+ ResizeSection<Rel>(elf_, relocations_section_, bytes);
+ RewriteSectionData(data, section_data, bytes);
+
+ // Nearly empty the current packed android relocations section. Leaves a
+ // four-byte stub so that some data remains allocated to the section.
+ // This is a convenience which allows us to re-pack this file again without
+ // having to remove the section and then add a new small one with objcopy.
+ // The way we resize sections relies on there being some data in a section.
+ data = GetSectionData(android_relocations_section_);
+ ResizeSection<Rel>(
+ elf_, android_relocations_section_, sizeof(kStubIdentifier));
+ RewriteSectionData(data, &kStubIdentifier, sizeof(kStubIdentifier));
+
+ // Rewrite .dynamic to remove two tags describing packed android relocations.
+ data = GetSectionData(dynamic_section_);
+ const ELF::Dyn* dynamic_base = reinterpret_cast<ELF::Dyn*>(data->d_buf);
+ std::vector<ELF::Dyn> dynamics(
+ dynamic_base,
+ dynamic_base + data->d_size / sizeof(dynamics[0]));
+ RemoveDynamicEntry(DT_ANDROID_REL_OFFSET, &dynamics);
+ RemoveDynamicEntry(DT_ANDROID_REL_SIZE, &dynamics);
+ const void* dynamics_data = &dynamics[0];
+ const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]);
+ RewriteSectionData(data, dynamics_data, dynamics_bytes);
+
+ Flush();
+ return true;
+}
+
+// Flush rewritten shared object file data.
+void ElfFile::Flush() {
+ // Flag all ELF data held in memory as needing to be written back to the
+ // file, and tell libelf that we have controlled the file layout.
+ elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY);
+ elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT);
+
+ // Write ELF data back to disk.
+ const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
+ CHECK(file_bytes > 0);
+ VLOG(1) << "elf_update returned: " << file_bytes;
+
+ // Clean up libelf, and truncate the output file to the number of bytes
+ // written by elf_update().
+ elf_end(elf_);
+ elf_ = NULL;
+ const int truncate = ftruncate(fd_, file_bytes);
+ CHECK(truncate == 0);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/elf_file.h b/tools/relocation_packer/src/elf_file.h
new file mode 100644
index 0000000..51239c0
--- /dev/null
+++ b/tools/relocation_packer/src/elf_file.h
@@ -0,0 +1,136 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ELF shared object file updates handler.
+//
+// Provides functions to remove relative relocations from the .rel.dyn
+// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn,
+// and unpack to return the file to its pre-packed state.
+//
+// Files to be packed or unpacked must include an existing .android.rel.dyn
+// or android.rela.dyn section. A standard libchrome.<version>.so will not
+// contain this section, so the following can be used to add one:
+//
+// echo -n 'NULL' >/tmp/small
+// if file libchrome.<version>.so | grep -q 'ELF 32'; then
+// arm-linux-androideabi-objcopy
+// --add-section .android.rel.dyn=/tmp/small
+// libchrome.<version>.so libchrome.<version>.so.packed
+// else
+// aarch64-linux-android-objcopy
+// --add-section .android.rela.dyn=/tmp/small
+// libchrome.<version>.so libchrome.<version>.so.packed
+// fi
+// rm /tmp/small
+//
+// To use, open the file and pass the file descriptor to the constructor,
+// then pack or unpack as desired. Packing or unpacking will flush the file
+// descriptor on success. Example:
+//
+// int fd = open(..., O_RDWR);
+// ElfFile elf_file(fd);
+// bool status;
+// if (is_packing)
+// status = elf_file.PackRelocations();
+// else
+// status = elf_file.UnpackRelocations();
+// close(fd);
+//
+// SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with
+// NONE-type entries rather than cutting a hole out of the shared object
+// file. This keeps all load addresses and offsets constant, and enables
+// easier debugging and testing.
+//
+// A packed shared object file has all of its relative relocations
+// removed from .rel.dyn or .rela.dyn, and replaced as packed data in
+// .android.rel.dyn or .android.rela.dyn respectively. The resulting file
+// is shorter than its non-packed original.
+//
+// Unpacking a packed file restores the file to its non-packed state, by
+// expanding the packed data in .android.rel.dyn or .android.rela.dyn,
+// combining the relative relocations with the data already in .rel.dyn
+// or .rela.dyn, and then writing back the now expanded section.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
+#define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
+
+#include <string.h>
+#include <vector>
+
+#include "elf.h"
+#include "libelf.h"
+#include "packer.h"
+
+namespace relocation_packer {
+
+// An ElfFile reads shared objects, and shuttles relative relocations
+// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn
+// sections.
+class ElfFile {
+ public:
+ explicit ElfFile(int fd)
+ : fd_(fd), is_padding_relocations_(false), elf_(NULL),
+ relocations_section_(NULL), dynamic_section_(NULL),
+ android_relocations_section_(NULL), relocations_type_(NONE) {}
+ ~ElfFile() {}
+
+ // Set padding mode. When padding, PackRelocations() will not shrink
+ // the .rel.dyn or .rela.dyn section, but instead replace relative with
+ // NONE-type entries.
+ // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it.
+ inline void SetPadding(bool flag) { is_padding_relocations_ = flag; }
+
+ // Transfer relative relocations from .rel.dyn or .rela.dyn to a packed
+ // representation in .android.rel.dyn or .android.rela.dyn. Returns true
+ // on success.
+ bool PackRelocations();
+
+ // Transfer relative relocations from a packed representation in
+ // .android.rel.dyn or .android.rela.dyn to .rel.dyn or .rela.dyn. Returns
+ // true on success.
+ bool UnpackRelocations();
+
+ private:
+ // Load a new ElfFile from a filedescriptor. If flushing, the file must
+ // be open for read/write. Returns true on successful ELF file load.
+ // |fd| is an open file descriptor for the shared object.
+ bool Load();
+
+ // Templated packer, helper for PackRelocations(). Rel type is one of
+ // ELF::Rel or ELF::Rela.
+ template <typename Rel>
+ bool PackTypedRelocations(const std::vector<Rel>& relocations,
+ Elf_Data* data);
+
+ // Templated unpacker, helper for UnpackRelocations(). Rel type is one of
+ // ELF::Rel or ELF::Rela.
+ template <typename Rel>
+ bool UnpackTypedRelocations(const std::vector<uint8_t>& packed,
+ Elf_Data* data);
+
+ // Write ELF file changes.
+ void Flush();
+
+ // File descriptor opened on the shared object.
+ int fd_;
+
+ // If set, pad rather than shrink .rel.dyn or .rela.dyn. Primarily for
+ // debugging, allows packing to be checked without affecting load addresses.
+ bool is_padding_relocations_;
+
+ // Libelf handle, assigned by Load().
+ Elf* elf_;
+
+ // Sections that we manipulate, assigned by Load().
+ Elf_Scn* relocations_section_;
+ Elf_Scn* dynamic_section_;
+ Elf_Scn* android_relocations_section_;
+
+ // Relocation type found, assigned by Load().
+ enum { NONE = 0, REL, RELA } relocations_type_;
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_
diff --git a/tools/relocation_packer/src/elf_file_unittest.cc b/tools/relocation_packer/src/elf_file_unittest.cc
new file mode 100644
index 0000000..37abd0d
--- /dev/null
+++ b/tools/relocation_packer/src/elf_file_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "elf_file.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include "debug.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Macro stringification.
+// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html
+#define XSTR(S) STR(S)
+#define STR(S) #S
+
+namespace {
+
+void GetDataFilePath(const char* name, std::string* path) {
+ std::string data_dir;
+
+ const char* bindir = getenv("bindir");
+ if (bindir) {
+ data_dir = std::string(bindir);
+ } else {
+ // Test data is in the gyp INTERMEDIATE_DIR subdirectory of the directory
+ // that contains the current binary.
+ char path[PATH_MAX];
+ memset(path, 0, sizeof(path));
+ ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1));
+
+ data_dir = std::string(path);
+ size_t pos = data_dir.rfind('/');
+ ASSERT_NE(std::string::npos, pos);
+
+ data_dir.erase(pos + 1);
+ data_dir += std::string(XSTR(INTERMEDIATE_DIR));
+ }
+
+ *path = data_dir + "/" + name;
+}
+
+void OpenRelocsTestFile(const char* name, FILE** stream) {
+ std::string path;
+ GetDataFilePath(name, &path);
+
+ FILE* testfile = fopen(path.c_str(), "rb");
+ ASSERT_FALSE(testfile == NULL);
+
+ FILE* temporary = tmpfile();
+ ASSERT_FALSE(temporary == NULL);
+
+ static const size_t buffer_size = 4096;
+ unsigned char buffer[buffer_size];
+
+ size_t bytes;
+ do {
+ bytes = fread(buffer, 1, sizeof(buffer), testfile);
+ ASSERT_EQ(bytes, fwrite(buffer, 1, bytes, temporary));
+ } while (bytes > 0);
+
+ ASSERT_EQ(0, fclose(testfile));
+ ASSERT_EQ(0, fseek(temporary, 0, SEEK_SET));
+ ASSERT_EQ(0, lseek(fileno(temporary), 0, SEEK_SET));
+
+ *stream = temporary;
+}
+
+void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) {
+ const char* arch = NULL;
+ if (ELF::kMachine == EM_ARM) {
+ arch = "arm32";
+ } else if (ELF::kMachine == EM_AARCH64) {
+ arch = "arm64";
+ }
+ ASSERT_FALSE(arch == NULL);
+
+ const std::string base = std::string("elf_file_unittest_relocs_") + arch;
+ const std::string relocs = base + ".so";
+ const std::string packed_relocs = base + "_packed.so";
+
+ OpenRelocsTestFile(relocs.c_str(), relocs_so);
+ OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so);
+}
+
+void CloseRelocsTestFile(FILE* temporary) {
+ fclose(temporary);
+}
+
+void CloseRelocsTestFiles(FILE* relocs_so, FILE* packed_relocs_so) {
+ CloseRelocsTestFile(relocs_so);
+ CloseRelocsTestFile(packed_relocs_so);
+}
+
+void CheckFileContentsEqual(FILE* first, FILE* second) {
+ ASSERT_EQ(0, fseek(first, 0, SEEK_SET));
+ ASSERT_EQ(0, fseek(second, 0, SEEK_SET));
+
+ static const size_t buffer_size = 4096;
+ unsigned char first_buffer[buffer_size];
+ unsigned char second_buffer[buffer_size];
+
+ do {
+ size_t first_read = fread(first_buffer, 1, sizeof(first_buffer), first);
+ size_t second_read = fread(second_buffer, 1, sizeof(second_buffer), second);
+
+ EXPECT_EQ(first_read, second_read);
+ EXPECT_EQ(0, memcmp(first_buffer, second_buffer, first_read));
+ } while (!feof(first) && !feof(second));
+
+ EXPECT_TRUE(feof(first) && feof(second));
+}
+
+} // namespace
+
+namespace relocation_packer {
+
+TEST(ElfFile, PackRelocations) {
+ ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
+
+ FILE* relocs_so = NULL;
+ FILE* packed_relocs_so = NULL;
+ OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
+ if (HasFatalFailure())
+ return;
+
+ ElfFile elf_file(fileno(relocs_so));
+
+ // Ensure unpacking fails (not packed).
+ EXPECT_FALSE(elf_file.UnpackRelocations());
+
+ // Pack relocations, and check files are now identical.
+ EXPECT_TRUE(elf_file.PackRelocations());
+ CheckFileContentsEqual(relocs_so, packed_relocs_so);
+
+ CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+TEST(ElfFile, UnpackRelocations) {
+ ASSERT_NE(EV_NONE, elf_version(EV_CURRENT));
+
+ FILE* relocs_so = NULL;
+ FILE* packed_relocs_so = NULL;
+ OpenRelocsTestFiles(&relocs_so, &packed_relocs_so);
+ if (HasFatalFailure())
+ return;
+
+ ElfFile elf_file(fileno(packed_relocs_so));
+
+ // Ensure packing fails (already packed).
+ EXPECT_FALSE(elf_file.PackRelocations());
+
+ // Unpack golden relocations, and check files are now identical.
+ EXPECT_TRUE(elf_file.UnpackRelocations());
+ CheckFileContentsEqual(packed_relocs_so, relocs_so);
+
+ CloseRelocsTestFiles(relocs_so, packed_relocs_so);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/elf_traits.h b/tools/relocation_packer/src/elf_traits.h
new file mode 100644
index 0000000..f099bab
--- /dev/null
+++ b/tools/relocation_packer/src/elf_traits.h
@@ -0,0 +1,105 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Target-specific ELF type traits.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
+#define TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
+
+#include "elf.h"
+#include "libelf.h"
+
+// The TARGET_ macro controls which Elf types we expect and handle.
+// Either TARGET_ARM or TARGET_ARM64 must be defined, but not both.
+
+#if !defined(TARGET_ARM) && !defined(TARGET_ARM64)
+# error "Unsupported target, define one of TARGET_ARM or TARGET_ARM64"
+#elif defined(TARGET_ARM) && defined(TARGET_ARM64)
+# error "Define one of TARGET_ARM or TARGET_ARM64, but not both"
+#endif
+
+// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h.
+#ifndef EM_AARCH64
+#define EM_AARCH64 183
+#endif
+#ifndef R_AARCH64_RELATIVE
+#define R_AARCH64_RELATIVE 1027
+#endif
+#ifndef R_AARCH64_NONE
+#define R_AARCH64_NONE 0
+#endif
+
+// ELF is a traits structure used to provide convenient aliases for
+// 32/64 bit Elf types and functions, depending on the target specified.
+
+#if defined(TARGET_ARM)
+struct ELF {
+ typedef Elf32_Addr Addr;
+ typedef Elf32_Dyn Dyn;
+ typedef Elf32_Ehdr Ehdr;
+ typedef Elf32_Off Off;
+ typedef Elf32_Phdr Phdr;
+ typedef Elf32_Rel Rel;
+ typedef Elf32_Rela Rela;
+ typedef Elf32_Shdr Shdr;
+ typedef Elf32_Sword Sword;
+ typedef Elf32_Sxword Sxword;
+ typedef Elf32_Sym Sym;
+ typedef Elf32_Word Word;
+ typedef Elf32_Xword Xword;
+
+ static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); }
+ static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); }
+ static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); }
+
+ enum { kMachine = EM_ARM };
+ enum { kFileClass = ELFCLASS32 };
+ enum { kRelativeRelocationCode = R_ARM_RELATIVE };
+ enum { kNoRelocationCode = R_ARM_NONE };
+ enum { kGnuStackSegmentAlignment = 0 };
+
+ static inline const char* Machine() { return "ARM"; }
+
+# define ELF_R_SYM(val) ELF32_R_SYM(val)
+# define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+# define ELF_R_INFO(sym, type) ELF32_R_INFO(sym, type)
+# define ELF_ST_TYPE(val) ELF32_ST_TYPE(val)
+};
+
+#elif defined(TARGET_ARM64)
+struct ELF {
+ typedef Elf64_Addr Addr;
+ typedef Elf64_Dyn Dyn;
+ typedef Elf64_Ehdr Ehdr;
+ typedef Elf64_Off Off;
+ typedef Elf64_Phdr Phdr;
+ typedef Elf64_Rel Rel;
+ typedef Elf64_Rela Rela;
+ typedef Elf64_Shdr Shdr;
+ typedef Elf64_Sword Sword;
+ typedef Elf64_Sxword Sxword;
+ typedef Elf64_Sym Sym;
+ typedef Elf64_Word Word;
+ typedef Elf64_Xword Xword;
+
+ static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); }
+ static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); }
+ static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); }
+
+ enum { kMachine = EM_AARCH64 };
+ enum { kFileClass = ELFCLASS64 };
+ enum { kRelativeRelocationCode = R_AARCH64_RELATIVE };
+ enum { kNoRelocationCode = R_AARCH64_NONE };
+ enum { kGnuStackSegmentAlignment = 16 };
+
+ static inline const char* Machine() { return "ARM64"; }
+
+# define ELF_R_SYM(val) ELF64_R_SYM(val)
+# define ELF_R_TYPE(val) ELF64_R_TYPE(val)
+# define ELF_R_INFO(sym, type) ELF64_R_INFO(sym, type)
+# define ELF_ST_TYPE(val) ELF64_ST_TYPE(val)
+};
+#endif
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_
diff --git a/tools/relocation_packer/src/leb128.cc b/tools/relocation_packer/src/leb128.cc
new file mode 100644
index 0000000..b48739c
--- /dev/null
+++ b/tools/relocation_packer/src/leb128.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "leb128.h"
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Empty constructor and destructor to silence chromium-style.
+Leb128Encoder::Leb128Encoder() { }
+Leb128Encoder::~Leb128Encoder() { }
+
+// Add a single value to the encoding. Values are encoded with variable
+// length. The least significant 7 bits of each byte hold 7 bits of data,
+// and the most significant bit is set on each byte except the last.
+void Leb128Encoder::Enqueue(ELF::Xword value) {
+ do {
+ const uint8_t byte = value & 127;
+ value >>= 7;
+ encoding_.push_back((value ? 128 : 0) | byte);
+ } while (value);
+}
+
+// Add a vector of values to the encoding.
+void Leb128Encoder::EnqueueAll(const std::vector<ELF::Xword>& values) {
+ for (size_t i = 0; i < values.size(); ++i)
+ Enqueue(values[i]);
+}
+
+// Create a new decoder for the given encoded stream.
+Leb128Decoder::Leb128Decoder(const std::vector<uint8_t>& encoding) {
+ encoding_ = encoding;
+ cursor_ = 0;
+}
+
+// Empty destructor to silence chromium-style.
+Leb128Decoder::~Leb128Decoder() { }
+
+// Decode and retrieve a single value from the encoding. Read forwards until
+// a byte without its most significant bit is found, then read the 7 bit
+// fields of the bytes spanned to re-form the value.
+ELF::Xword Leb128Decoder::Dequeue() {
+ ELF::Xword value = 0;
+
+ size_t shift = 0;
+ uint8_t byte;
+
+ // Loop until we reach a byte with its high order bit clear.
+ do {
+ byte = encoding_[cursor_++];
+ value |= static_cast<ELF::Xword>(byte & 127) << shift;
+ shift += 7;
+ } while (byte & 128);
+
+ return value;
+}
+
+// Decode and retrieve all remaining values from the encoding.
+void Leb128Decoder::DequeueAll(std::vector<ELF::Xword>* values) {
+ while (cursor_ < encoding_.size())
+ values->push_back(Dequeue());
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/leb128.h b/tools/relocation_packer/src/leb128.h
new file mode 100644
index 0000000..6cc2d7c
--- /dev/null
+++ b/tools/relocation_packer/src/leb128.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// LEB128 encoder and decoder for packed relative relocations.
+//
+// Run-length encoded relative relocations consist of a large number
+// of pairs of relatively small positive integer values. Encoding these as
+// LEB128 saves space.
+//
+// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
+#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode packed words as a LEB128 byte stream.
+class Leb128Encoder {
+ public:
+ // Explicit (but empty) constructor and destructor, for chromium-style.
+ Leb128Encoder();
+ ~Leb128Encoder();
+
+ // Add a value to the encoding stream.
+ // |value| is the unsigned int to add.
+ void Enqueue(ELF::Xword value);
+
+ // Add a vector of values to the encoding stream.
+ // |values| is the vector of unsigned ints to add.
+ void EnqueueAll(const std::vector<ELF::Xword>& values);
+
+ // Retrieve the encoded representation of the values.
+ // |encoding| is the returned vector of encoded data.
+ void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+ // Growable vector holding the encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+};
+
+// Decode a LEB128 byte stream to produce packed words.
+class Leb128Decoder {
+ public:
+ // Create a new decoder for the given encoded stream.
+ // |encoding| is the vector of encoded data.
+ explicit Leb128Decoder(const std::vector<uint8_t>& encoding);
+
+ // Explicit (but empty) destructor, for chromium-style.
+ ~Leb128Decoder();
+
+ // Retrieve the next value from the encoded stream.
+ ELF::Xword Dequeue();
+
+ // Retrieve all remaining values from the encoded stream.
+ // |values| is the vector of decoded data.
+ void DequeueAll(std::vector<ELF::Xword>* values);
+
+ private:
+ // Encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+
+ // Cursor indicating the current stream retrieval point.
+ size_t cursor_;
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_
diff --git a/tools/relocation_packer/src/leb128_unittest.cc b/tools/relocation_packer/src/leb128_unittest.cc
new file mode 100644
index 0000000..bd607b7
--- /dev/null
+++ b/tools/relocation_packer/src/leb128_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "leb128.h"
+
+#include <vector>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Leb128, Encoder) {
+ std::vector<ELF::Xword> values;
+ values.push_back(624485);
+ values.push_back(0);
+ values.push_back(1);
+ values.push_back(127);
+ values.push_back(128);
+
+ Leb128Encoder encoder;
+ encoder.EnqueueAll(values);
+
+ encoder.Enqueue(4294967295);
+ encoder.Enqueue(18446744073709551615ul);
+
+ std::vector<uint8_t> encoding;
+ encoder.GetEncoding(&encoding);
+
+ EXPECT_EQ(23, encoding.size());
+ // 624485
+ EXPECT_EQ(0xe5, encoding[0]);
+ EXPECT_EQ(0x8e, encoding[1]);
+ EXPECT_EQ(0x26, encoding[2]);
+ // 0
+ EXPECT_EQ(0x00, encoding[3]);
+ // 1
+ EXPECT_EQ(0x01, encoding[4]);
+ // 127
+ EXPECT_EQ(0x7f, encoding[5]);
+ // 128
+ EXPECT_EQ(0x80, encoding[6]);
+ EXPECT_EQ(0x01, encoding[7]);
+ // 4294967295
+ EXPECT_EQ(0xff, encoding[8]);
+ EXPECT_EQ(0xff, encoding[9]);
+ EXPECT_EQ(0xff, encoding[10]);
+ EXPECT_EQ(0xff, encoding[11]);
+ EXPECT_EQ(0x0f, encoding[12]);
+ // 18446744073709551615
+ EXPECT_EQ(0xff, encoding[13]);
+ EXPECT_EQ(0xff, encoding[14]);
+ EXPECT_EQ(0xff, encoding[15]);
+ EXPECT_EQ(0xff, encoding[16]);
+ EXPECT_EQ(0xff, encoding[17]);
+ EXPECT_EQ(0xff, encoding[18]);
+ EXPECT_EQ(0xff, encoding[19]);
+ EXPECT_EQ(0xff, encoding[20]);
+ EXPECT_EQ(0xff, encoding[21]);
+ EXPECT_EQ(0x01, encoding[22]);
+}
+
+TEST(Leb128, Decoder) {
+ std::vector<uint8_t> encoding;
+ // 624485
+ encoding.push_back(0xe5);
+ encoding.push_back(0x8e);
+ encoding.push_back(0x26);
+ // 0
+ encoding.push_back(0x00);
+ // 1
+ encoding.push_back(0x01);
+ // 127
+ encoding.push_back(0x7f);
+ // 128
+ encoding.push_back(0x80);
+ encoding.push_back(0x01);
+ // 4294967295
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0x0f);
+ // 18446744073709551615
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0x01);
+
+ Leb128Decoder decoder(encoding);
+
+ EXPECT_EQ(624485, decoder.Dequeue());
+
+ std::vector<ELF::Xword> dequeued;
+ decoder.DequeueAll(&dequeued);
+
+ EXPECT_EQ(6, dequeued.size());
+ EXPECT_EQ(0, dequeued[0]);
+ EXPECT_EQ(1, dequeued[1]);
+ EXPECT_EQ(127, dequeued[2]);
+ EXPECT_EQ(128, dequeued[3]);
+ EXPECT_EQ(4294967295, dequeued[4]);
+ EXPECT_EQ(18446744073709551615ul, dequeued[5]);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/main.cc b/tools/relocation_packer/src/main.cc
new file mode 100644
index 0000000..28f5b04
--- /dev/null
+++ b/tools/relocation_packer/src/main.cc
@@ -0,0 +1,175 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tool to pack and unpack relative relocations in a shared library.
+//
+// Packing removes relative relocations from .rel.dyn and writes them
+// in a more compact form to .android.rel.dyn. Unpacking does the reverse.
+//
+// Invoke with -v to trace actions taken when packing or unpacking.
+// Invoke with -p to pad removed relocations with R_*_NONE. Suppresses
+// shrinking of .rel.dyn.
+// See PrintUsage() below for full usage details.
+//
+// NOTE: Breaks with libelf 0.152, which is buggy. libelf 0.158 works.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+
+#include "debug.h"
+#include "elf_file.h"
+#include "libelf.h"
+
+namespace {
+
+void PrintUsage(const char* argv0) {
+ std::string temporary = argv0;
+ const size_t last_slash = temporary.find_last_of("/");
+ if (last_slash != temporary.npos) {
+ temporary.erase(0, last_slash + 1);
+ }
+ const char* basename = temporary.c_str();
+
+ printf(
+ "Usage: %s [-u] [-v] [-p] file\n\n"
+ "Pack or unpack relative relocations in a shared library.\n\n"
+ " -u, --unpack unpack previously packed relative relocations\n"
+ " -v, --verbose trace object file modifications (for debugging)\n"
+ " -p, --pad do not shrink relocations, but pad (for debugging)\n\n",
+ basename);
+
+ if (ELF::kMachine == EM_ARM) {
+ printf(
+ "Extracts relative relocations from the .rel.dyn section, packs them\n"
+ "into a more compact format, and stores the packed relocations in\n"
+ ".android.rel.dyn. Expands .android.rel.dyn to hold the packed\n"
+ "data, and shrinks .rel.dyn by the amount of unpacked data removed\n"
+ "from it.\n\n"
+ "Before being packed, a shared library needs to be prepared by adding\n"
+ "a null .android.rel.dyn section.\n\n"
+ "To pack relocations in a shared library:\n\n"
+ " echo -n 'NULL' >/tmp/small\n"
+ " arm-linux-androideabi-objcopy \\\n"
+ " --add-section .android.rel.dyn=/tmp/small \\\n"
+ " libchrome.<version>.so\n"
+ " rm /tmp/small\n"
+ " %s libchrome.<version>.so\n\n"
+ "To unpack and restore the shared library to its original state:\n\n"
+ " %s -u libchrome.<version>.so\n"
+ " arm-linux-androideabi-objcopy \\\n"
+ " --remove-section=.android.rel.dyn libchrome.<version>.so\n\n",
+ basename, basename);
+ } else if (ELF::kMachine == EM_AARCH64) {
+ printf(
+ "Extracts relative relocations from the .rela.dyn section, packs them\n"
+ "into a more compact format, and stores the packed relocations in\n"
+ ".android.rela.dyn. Expands .android.rela.dyn to hold the packed\n"
+ "data, and shrinks .rela.dyn by the amount of unpacked data removed\n"
+ "from it.\n\n"
+ "Before being packed, a shared library needs to be prepared by adding\n"
+ "a null .android.rela.dyn section.\n\n"
+ "To pack relocations in a shared library:\n\n"
+ " echo -n 'NULL' >/tmp/small\n"
+ " aarch64-linux-android-objcopy \\\n"
+ " --add-section .android.rela.dyn=/tmp/small \\\n"
+ " libchrome.<version>.so\n"
+ " rm /tmp/small\n"
+ " %s libchrome.<version>.so\n\n"
+ "To unpack and restore the shared library to its original state:\n\n"
+ " %s -u libchrome.<version>.so\n"
+ " aarch64-linux-android-objcopy \\\n"
+ " --remove-section=.android.rela.dyn libchrome.<version>.so\n\n",
+ basename, basename);
+ } else {
+ NOTREACHED();
+ }
+
+ printf(
+ "Debug sections are not handled, so packing should not be used on\n"
+ "shared libraries compiled for debugging or otherwise unstripped.\n");
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ bool is_unpacking = false;
+ bool is_verbose = false;
+ bool is_padding = false;
+
+ static const option options[] = {
+ {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'},
+ {"help", 0, 0, 'h'}, {NULL, 0, 0, 0}
+ };
+ bool has_options = true;
+ while (has_options) {
+ int c = getopt_long(argc, argv, "uvph", options, NULL);
+ switch (c) {
+ case 'u':
+ is_unpacking = true;
+ break;
+ case 'v':
+ is_verbose = true;
+ break;
+ case 'p':
+ is_padding = true;
+ break;
+ case 'h':
+ PrintUsage(argv[0]);
+ return 0;
+ case '?':
+ LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
+ return 1;
+ case -1:
+ has_options = false;
+ break;
+ default:
+ NOTREACHED();
+ return 1;
+ }
+ }
+ if (optind != argc - 1) {
+ LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
+ return 1;
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ LOG(WARNING) << "Elf Library is out of date!";
+ }
+
+ LOG(INFO) << "Configured for " << ELF::Machine();
+
+ const char* file = argv[argc - 1];
+ const int fd = open(file, O_RDWR);
+ if (fd == -1) {
+ LOG(ERROR) << file << ": " << strerror(errno);
+ return 1;
+ }
+
+ if (is_verbose)
+ relocation_packer::Logger::SetVerbose(1);
+
+ relocation_packer::ElfFile elf_file(fd);
+ elf_file.SetPadding(is_padding);
+
+ bool status;
+ if (is_unpacking)
+ status = elf_file.UnpackRelocations();
+ else
+ status = elf_file.PackRelocations();
+
+ close(fd);
+
+ if (!status) {
+ LOG(ERROR) << file << ": failed to pack/unpack file";
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/tools/relocation_packer/src/packer.cc b/tools/relocation_packer/src/packer.cc
new file mode 100644
index 0000000..29bec1e
--- /dev/null
+++ b/tools/relocation_packer/src/packer.cc
@@ -0,0 +1,124 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "packer.h"
+
+#include <vector>
+
+#include "debug.h"
+#include "delta_encoder.h"
+#include "elf_traits.h"
+#include "leb128.h"
+#include "run_length_encoder.h"
+#include "sleb128.h"
+
+namespace relocation_packer {
+
+// Pack relative relocations into a run-length encoded packed
+// representation.
+void RelocationPacker::PackRelativeRelocations(
+ const std::vector<ELF::Rel>& relocations,
+ std::vector<uint8_t>* packed) {
+ // Run-length encode.
+ std::vector<ELF::Xword> packed_words;
+ RelocationRunLengthCodec codec;
+ codec.Encode(relocations, &packed_words);
+
+ // If insufficient data to run-length encode, do nothing.
+ if (packed_words.empty())
+ return;
+
+ // LEB128 encode, with "APR1" prefix.
+ Leb128Encoder encoder;
+ encoder.Enqueue('A');
+ encoder.Enqueue('P');
+ encoder.Enqueue('R');
+ encoder.Enqueue('1');
+ encoder.EnqueueAll(packed_words);
+
+ encoder.GetEncoding(packed);
+
+ // Pad packed to a whole number of words. This padding will decode as
+ // LEB128 zeroes. Run-length decoding ignores it because encoding
+ // embeds the pairs count in the stream itself.
+ while (packed->size() % sizeof(ELF::Word))
+ packed->push_back(0);
+}
+
+// Unpack relative relocations from a run-length encoded packed
+// representation.
+void RelocationPacker::UnpackRelativeRelocations(
+ const std::vector<uint8_t>& packed,
+ std::vector<ELF::Rel>* relocations) {
+ // LEB128 decode, after checking and stripping "APR1" prefix.
+ std::vector<ELF::Xword> packed_words;
+ Leb128Decoder decoder(packed);
+ CHECK(decoder.Dequeue() == 'A' &&
+ decoder.Dequeue() == 'P' &&
+ decoder.Dequeue() == 'R' &&
+ decoder.Dequeue() == '1');
+ decoder.DequeueAll(&packed_words);
+
+ // Run-length decode.
+ RelocationRunLengthCodec codec;
+ codec.Decode(packed_words, relocations);
+}
+
+// Pack relative relocations with addends into a delta encoded packed
+// representation.
+void RelocationPacker::PackRelativeRelocations(
+ const std::vector<ELF::Rela>& relocations,
+ std::vector<uint8_t>* packed) {
+ // Delta encode.
+ std::vector<ELF::Sxword> packed_words;
+ RelocationDeltaCodec codec;
+ codec.Encode(relocations, &packed_words);
+
+ // If insufficient data to delta encode, do nothing.
+ if (packed_words.empty())
+ return;
+
+ // Signed LEB128 encode, with "APA1" prefix. ASCII does not encode as
+ // itself under signed LEB128, so we have to treat it specially.
+ Sleb128Encoder encoder;
+ encoder.EnqueueAll(packed_words);
+ std::vector<uint8_t> encoded;
+ encoder.GetEncoding(&encoded);
+
+ packed->push_back('A');
+ packed->push_back('P');
+ packed->push_back('A');
+ packed->push_back('1');
+ packed->insert(packed->end(), encoded.begin(), encoded.end());
+
+ // Pad packed to a whole number of words. This padding will decode as
+ // signed LEB128 zeroes. Delta decoding ignores it because encoding
+ // embeds the pairs count in the stream itself.
+ while (packed->size() % sizeof(ELF::Word))
+ packed->push_back(0);
+}
+
+// Unpack relative relocations with addends from a delta encoded
+// packed representation.
+void RelocationPacker::UnpackRelativeRelocations(
+ const std::vector<uint8_t>& packed,
+ std::vector<ELF::Rela>* relocations) {
+ // Check "APA1" prefix.
+ CHECK(packed.at(0) == 'A' &&
+ packed.at(1) == 'P' &&
+ packed.at(2) == 'A' &&
+ packed.at(3) == '1');
+
+ // Signed LEB128 decode, after stripping "APA1" prefix.
+ std::vector<ELF::Sxword> packed_words;
+ std::vector<uint8_t> stripped(packed.begin() + 4, packed.end());
+ Sleb128Decoder decoder(stripped);
+ decoder.DequeueAll(&packed_words);
+
+ // Delta decode.
+ RelocationDeltaCodec codec;
+ codec.Decode(packed_words, relocations);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/packer.h b/tools/relocation_packer/src/packer.h
new file mode 100644
index 0000000..db09ce8
--- /dev/null
+++ b/tools/relocation_packer/src/packer.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Pack relative relocations into a more compact form.
+//
+//
+// For relative relocations without addends (32 bit platforms)
+// -----------------------------------------------------------
+//
+// Applies two packing strategies. The first is run-length encoding, which
+// turns a large set of relative relocations into a much smaller set
+// of delta-count pairs, prefixed with a two-word header comprising the
+// count of pairs and the initial relocation offset. The second is LEB128
+// encoding, which compresses the result of run-length encoding.
+//
+// Once packed, data is prefixed by an identifier that allows for any later
+// versioning of packing strategies.
+//
+// A complete packed stream of relocations without addends might look
+// something like:
+//
+// "APR1" pairs init_offset count1 delta1 count2 delta2 ...
+// 41505231 f2b003 b08ac716 e001 04 01 10 ...
+//
+//
+// For relative relocations with addends (64 bit platforms)
+// --------------------------------------------------------
+//
+// Applies two packing strategies. The first is delta encoding, which
+// turns a large set of relative relocations into a smaller set
+// of offset and addend delta pairs, prefixed with a header indicating the
+// count of pairs. The second is signed LEB128 encoding, which compacts
+// the result of delta encoding.
+//
+// Once packed, data is prefixed by an identifier that allows for any later
+// versioning of packing strategies.
+//
+// A complete packed stream might look something like:
+//
+// "APA1" pairs offset_d1 addend_d1 offset_d2 addend_d2 ...
+// 41505232 f2b018 04 28 08 9f01 ...
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
+
+#include <stdint.h>
+#include <vector>
+
+#include "elf.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// A RelocationPacker packs vectors of relative relocations into more
+// compact forms, and unpacks them to reproduce the pre-packed data.
+class RelocationPacker {
+ public:
+ // Pack relative relocations into a more compact form.
+ // |relocations| is a vector of relative relocation structs.
+ // |packed| is the vector of packed bytes into which relocations are packed.
+ static void PackRelativeRelocations(const std::vector<ELF::Rel>& relocations,
+ std::vector<uint8_t>* packed);
+ static void PackRelativeRelocations(const std::vector<ELF::Rela>& relocations,
+ std::vector<uint8_t>* packed);
+
+ // Unpack relative relocations from their more compact form.
+ // |packed| is the vector of packed relocations.
+ // |relocations| is a vector of unpacked relative relocation structs.
+ static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
+ std::vector<ELF::Rel>* relocations);
+ static void UnpackRelativeRelocations(const std::vector<uint8_t>& packed,
+ std::vector<ELF::Rela>* relocations);
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_
diff --git a/tools/relocation_packer/src/packer_unittest.cc b/tools/relocation_packer/src/packer_unittest.cc
new file mode 100644
index 0000000..de5be79
--- /dev/null
+++ b/tools/relocation_packer/src/packer_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "packer.h"
+
+#include <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
+ ELF::Rel relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocations->push_back(relocation);
+}
+
+bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
+ return relocation.r_offset == addr &&
+ ELF_R_SYM(relocation.r_info) == 0 &&
+ ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
+}
+
+void AddRelocation(ELF::Addr addr,
+ ELF::Sxword addend,
+ std::vector<ELF::Rela>* relocations) {
+ ELF::Rela relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocation.r_addend = addend;
+ relocations->push_back(relocation);
+}
+
+bool CheckRelocation(ELF::Addr addr,
+ ELF::Sxword addend,
+ const ELF::Rela& relocation) {
+ return relocation.r_offset == addr &&
+ ELF_R_SYM(relocation.r_info) == 0 &&
+ ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode &&
+ relocation.r_addend == addend;
+}
+
+} // namespace
+
+namespace relocation_packer {
+
+TEST(Packer, PackRel) {
+ std::vector<ELF::Rel> relocations;
+ std::vector<uint8_t> packed;
+
+ RelocationPacker packer;
+
+ // Initial relocation.
+ AddRelocation(0xd1ce0000, &relocations);
+ // Two more relocations, 4 byte deltas.
+ AddRelocation(0xd1ce0004, &relocations);
+ AddRelocation(0xd1ce0008, &relocations);
+ // Three more relocations, 8 byte deltas.
+ AddRelocation(0xd1ce0010, &relocations);
+ AddRelocation(0xd1ce0018, &relocations);
+ AddRelocation(0xd1ce0020, &relocations);
+
+ packed.clear();
+ packer.PackRelativeRelocations(relocations, &packed);
+
+ EXPECT_EQ(16, packed.size());
+ // Identifier.
+ EXPECT_EQ('A', packed[0]);
+ EXPECT_EQ('P', packed[1]);
+ EXPECT_EQ('R', packed[2]);
+ EXPECT_EQ('1', packed[3]);
+ // Count-delta pairs count.
+ EXPECT_EQ(2, packed[4]);
+ // 0xd1ce0000
+ EXPECT_EQ(128, packed[5]);
+ EXPECT_EQ(128, packed[6]);
+ EXPECT_EQ(184, packed[7]);
+ EXPECT_EQ(142, packed[8]);
+ EXPECT_EQ(13, packed[9]);
+ // Run of two relocations, 4 byte deltas.
+ EXPECT_EQ(2, packed[10]);
+ EXPECT_EQ(4, packed[11]);
+ // Run of three relocations, 8 byte deltas.
+ EXPECT_EQ(3, packed[12]);
+ EXPECT_EQ(8, packed[13]);
+ // Padding.
+ EXPECT_EQ(0, packed[14]);
+ EXPECT_EQ(0, packed[15]);
+}
+
+TEST(Packer, UnpackRel) {
+ std::vector<uint8_t> packed;
+ std::vector<ELF::Rel> relocations;
+
+ RelocationPacker packer;
+
+ // Identifier.
+ packed.push_back('A');
+ packed.push_back('P');
+ packed.push_back('R');
+ packed.push_back('1');
+ // Count-delta pairs count.
+ packed.push_back(2);
+ // 0xd1ce0000
+ packed.push_back(128);
+ packed.push_back(128);
+ packed.push_back(184);
+ packed.push_back(142);
+ packed.push_back(13);
+ // Run of two relocations, 4 byte deltas.
+ packed.push_back(2);
+ packed.push_back(4);
+ // Run of three relocations, 8 byte deltas.
+ packed.push_back(3);
+ packed.push_back(8);
+ // Padding.
+ packed.push_back(0);
+ packed.push_back(0);
+
+ relocations.clear();
+ packer.UnpackRelativeRelocations(packed, &relocations);
+
+ EXPECT_EQ(6, relocations.size());
+ // Initial relocation.
+ EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0]));
+ // Two relocations, 4 byte deltas.
+ EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1]));
+ EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2]));
+ // Three relocations, 8 byte deltas.
+ EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3]));
+ EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4]));
+ EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5]));
+}
+
+TEST(Packer, PackRela) {
+ std::vector<ELF::Rela> relocations;
+ std::vector<uint8_t> packed;
+
+ RelocationPacker packer;
+
+ // Initial relocation.
+ AddRelocation(0xd1ce0000, 10000, &relocations);
+ // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+ AddRelocation(0xd1ce0004, 10012, &relocations);
+ AddRelocation(0xd1ce0008, 10024, &relocations);
+ // Three more relocations, 8 byte deltas, -24 byte addend deltas.
+ AddRelocation(0xd1ce0010, 10000, &relocations);
+ AddRelocation(0xd1ce0018, 9976, &relocations);
+ AddRelocation(0xd1ce0020, 9952, &relocations);
+
+ packed.clear();
+ packer.PackRelativeRelocations(relocations, &packed);
+
+ EXPECT_EQ(24, packed.size());
+ // Identifier.
+ EXPECT_EQ('A', packed[0]);
+ EXPECT_EQ('P', packed[1]);
+ EXPECT_EQ('A', packed[2]);
+ EXPECT_EQ('1', packed[3]);
+ // Delta pairs count.
+ EXPECT_EQ(6, packed[4]);
+ // 0xd1ce0000
+ EXPECT_EQ(128, packed[5]);
+ EXPECT_EQ(128, packed[6]);
+ EXPECT_EQ(184, packed[7]);
+ EXPECT_EQ(142, packed[8]);
+ EXPECT_EQ(13, packed[9]);
+ // 10000
+ EXPECT_EQ(144, packed[10]);
+ EXPECT_EQ(206, packed[11]);
+ EXPECT_EQ(0, packed[12]);
+ // 4, 12
+ EXPECT_EQ(4, packed[13]);
+ EXPECT_EQ(12, packed[14]);
+ // 4, 12
+ EXPECT_EQ(4, packed[15]);
+ EXPECT_EQ(12, packed[16]);
+ // 8, -24
+ EXPECT_EQ(8, packed[17]);
+ EXPECT_EQ(104, packed[18]);
+ // 8, -24
+ EXPECT_EQ(8, packed[19]);
+ EXPECT_EQ(104, packed[20]);
+ // 8, -24
+ EXPECT_EQ(8, packed[21]);
+ EXPECT_EQ(104, packed[22]);
+ // Padding.
+ EXPECT_EQ(0, packed[23]);
+}
+
+TEST(Packer, UnpackRela) {
+ std::vector<uint8_t> packed;
+ std::vector<ELF::Rela> relocations;
+
+ RelocationPacker packer;
+
+ // Identifier.
+ packed.push_back('A');
+ packed.push_back('P');
+ packed.push_back('A');
+ packed.push_back('1');
+ // Delta pairs count.
+ packed.push_back(6);
+ // 0xd1ce0000
+ packed.push_back(128);
+ packed.push_back(128);
+ packed.push_back(184);
+ packed.push_back(142);
+ packed.push_back(13);
+ // 10000
+ packed.push_back(144);
+ packed.push_back(206);
+ packed.push_back(0);
+ // 4, 12
+ packed.push_back(4);
+ packed.push_back(12);
+ // 4, 12
+ packed.push_back(4);
+ packed.push_back(12);
+ // 8, -24
+ packed.push_back(8);
+ packed.push_back(104);
+ // 8, -24
+ packed.push_back(8);
+ packed.push_back(104);
+ // 8, -24
+ packed.push_back(8);
+ packed.push_back(104);
+ // Padding.
+ packed.push_back(0);
+
+ relocations.clear();
+ packer.UnpackRelativeRelocations(packed, &relocations);
+
+ EXPECT_EQ(6, relocations.size());
+ // Initial relocation.
+ EXPECT_TRUE(CheckRelocation(0xd1ce0000, 10000, relocations[0]));
+ // Two more relocations, 4 byte offset deltas, 12 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation(0xd1ce0004, 10012, relocations[1]));
+ EXPECT_TRUE(CheckRelocation(0xd1ce0008, 10024, relocations[2]));
+ // Three more relocations, 8 byte offset deltas, -24 byte addend deltas.
+ EXPECT_TRUE(CheckRelocation(0xd1ce0010, 10000, relocations[3]));
+ EXPECT_TRUE(CheckRelocation(0xd1ce0018, 9976, relocations[4]));
+ EXPECT_TRUE(CheckRelocation(0xd1ce0020, 9952, relocations[5]));
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/run_all_unittests.cc b/tools/relocation_packer/src/run_all_unittests.cc
new file mode 100644
index 0000000..4122be1
--- /dev/null
+++ b/tools/relocation_packer/src/run_all_unittests.cc
@@ -0,0 +1,10 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/tools/relocation_packer/src/run_length_encoder.cc b/tools/relocation_packer/src/run_length_encoder.cc
new file mode 100644
index 0000000..2f2e1c3
--- /dev/null
+++ b/tools/relocation_packer/src/run_length_encoder.cc
@@ -0,0 +1,144 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "run_length_encoder.h"
+
+#include <vector>
+
+#include "debug.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+namespace {
+
+// Generate a vector of deltas between the r_offset fields of adjacent
+// relative relocations.
+void GetDeltas(const std::vector<ELF::Rel>& relocations,
+ std::vector<ELF::Addr>* deltas) {
+ CHECK(relocations.size() >= 2);
+
+ for (size_t i = 0; i < relocations.size() - 1; ++i) {
+ const ELF::Rel* first = &relocations[i];
+ CHECK(ELF_R_TYPE(first->r_info) == ELF::kRelativeRelocationCode);
+
+ const ELF::Rel* second = &relocations[i + 1];
+ CHECK(ELF_R_TYPE(second->r_info) == ELF::kRelativeRelocationCode);
+
+ // Requires that offsets are 'strictly increasing'. The packing
+ // algorithm fails if this does not hold.
+ CHECK(second->r_offset > first->r_offset);
+ deltas->push_back(second->r_offset - first->r_offset);
+ }
+}
+
+// Condense a set of r_offset deltas into a run-length encoded packing.
+// Represented as count-delta pairs, where count is the run length and
+// delta the common difference between adjacent r_offsets.
+void Condense(const std::vector<ELF::Addr>& deltas,
+ std::vector<ELF::Xword>* packed) {
+ CHECK(!deltas.empty());
+ size_t count = 0;
+ ELF::Addr current = deltas[0];
+
+ // Identify spans of identically valued deltas.
+ for (size_t i = 0; i < deltas.size(); ++i) {
+ const ELF::Addr delta = deltas[i];
+ if (delta == current) {
+ count++;
+ } else {
+ // We reached the end of a span of identically valued deltas.
+ packed->push_back(count);
+ packed->push_back(current);
+ current = delta;
+ count = 1;
+ }
+ }
+
+ // Write the final span.
+ packed->push_back(count);
+ packed->push_back(current);
+}
+
+// Uncondense a set of r_offset deltas from a run-length encoded packing.
+// The initial address for uncondensing, the start index for the first
+// condensed slot in packed, and the count of pairs are provided.
+void Uncondense(ELF::Addr addr,
+ const std::vector<ELF::Xword>& packed,
+ size_t start_index,
+ size_t end_index,
+ std::vector<ELF::Rel>* relocations) {
+ // The first relocation is just one created from the initial address.
+ ELF::Rel initial;
+ initial.r_offset = addr;
+ initial.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocations->push_back(initial);
+
+ // Read each count and delta pair, beginning at the start index and
+ // finishing at the end index.
+ for (size_t i = start_index; i < end_index; i += 2) {
+ size_t count = packed[i];
+ const ELF::Addr delta = packed[i + 1];
+ CHECK(count > 0 && delta > 0);
+
+ // Generate relocations for this count and delta pair.
+ while (count) {
+ addr += delta;
+ ELF::Rel relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocations->push_back(relocation);
+ count--;
+ }
+ }
+}
+
+} // namespace
+
+// Encode relative relocations into a run-length encoded (packed)
+// representation.
+void RelocationRunLengthCodec::Encode(const std::vector<ELF::Rel>& relocations,
+ std::vector<ELF::Xword>* packed) {
+ // If we have zero or one relocation only then there is no packing
+ // possible; a run-length encoding needs a run.
+ if (relocations.size() < 2)
+ return;
+
+ std::vector<ELF::Addr> deltas;
+ GetDeltas(relocations, &deltas);
+
+ // Reserve space for the element count.
+ packed->push_back(0);
+
+ // Initialize the packed data with the first offset, then follow up with
+ // the condensed deltas vector.
+ packed->push_back(relocations[0].r_offset);
+ Condense(deltas, packed);
+
+ // Fill in the packed pair count.
+ packed->at(0) = (packed->size() - 2) >> 1;
+}
+
+// Decode relative relocations from a run-length encoded (packed)
+// representation.
+void RelocationRunLengthCodec::Decode(const std::vector<ELF::Xword>& packed,
+ std::vector<ELF::Rel>* relocations) {
+ // We need at least one packed pair after the packed pair count and start
+ // address to be able to unpack.
+ if (packed.size() < 4)
+ return;
+
+ // Ensure that the packed data offers enough pairs. There may be zero
+ // padding on it that we ignore.
+ CHECK(packed[0] <= (packed.size() - 2) >> 1);
+
+ // The first packed vector element is the pairs count and the second the
+ // initial address. Start uncondensing pairs at the third, and finish
+ // at the end of the pairs data.
+ const size_t pairs_count = packed[0];
+ const ELF::Addr addr = packed[1];
+ Uncondense(addr, packed, 2, 2 + (pairs_count << 1), relocations);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/run_length_encoder.h b/tools/relocation_packer/src/run_length_encoder.h
new file mode 100644
index 0000000..f3a80e6
--- /dev/null
+++ b/tools/relocation_packer/src/run_length_encoder.h
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Run-length encode and decode relative relocations.
+//
+// Relative relocations are the bulk of dynamic relocations (the
+// .rel.dyn or .rela.dyn sections) in libchrome.<version>.so, and the ELF
+// standard representation of them is wasteful. .rel.dyn contains
+// relocations without addends, .rela.dyn relocations with addends.
+//
+// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes
+// on 64 bit plaforms, split into offset and info fields. Offsets strictly
+// increase, and each is commonly a few bytes different from its predecessor.
+// There are long runs where the difference does not change. The info field
+// is constant. Example, from 'readelf -x4 libchrome.<version>.so' 32 bit:
+//
+// offset info offset info
+// 808fef01 17000000 848fef01 17000000 ................
+// 888fef01 17000000 8c8fef01 17000000 ................
+// 908fef01 17000000 948fef01 17000000 ................
+//
+// Run length encoding packs this data more efficiently, by representing it
+// as a delta and a count of entries each differing from its predecessor
+// by this delta. The above can be represented as a start address followed
+// by an encoded count of 6 and offset difference of 4:
+//
+// start count diff
+// 01ef8f80 00000006 00000004
+//
+// Because relative relocation offsets strictly increase, the complete
+// set of relative relocations in libchrome.<version>.so can be
+// represented by a single start address followed by one or more difference
+// and count encoded word pairs:
+//
+// start run1 count run1 diff run2 count run2 diff
+// 01ef8f80 00000006 00000004 00000010 00000008 ...
+//
+// Decoding regenerates relative relocations beginning at address
+// 'start' and for each encoded run, incrementing the address by 'difference'
+// for 'count' iterations and emitting a new relative relocation.
+//
+// Once encoded, data is prefixed by a single word count of packed delta and
+// count pairs. A final run-length encoded relative relocations vector
+// might therefore look something like:
+//
+// pairs start run 1 run 2 ... run 15
+// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ...
+// Interpreted as:
+// pairs=15 start=.. count=6,delta=4 count=16,delta=8
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
+#define TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
+
+#include <vector>
+
+#include "elf.h"
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// A RelocationRunLengthCodec packs vectors of relative relocations
+// into more compact forms, and unpacks them to reproduce the pre-packed data.
+class RelocationRunLengthCodec {
+ public:
+ // Encode relative relocations into a more compact form.
+ // |relocations| is a vector of relative relocation structs.
+ // |packed| is the vector of packed words into which relocations are packed.
+ static void Encode(const std::vector<ELF::Rel>& relocations,
+ std::vector<ELF::Xword>* packed);
+
+ // Decode relative relocations from their more compact form.
+ // |packed| is the vector of packed relocations.
+ // |relocations| is a vector of unpacked relative relocation structs.
+ static void Decode(const std::vector<ELF::Xword>& packed,
+ std::vector<ELF::Rel>* relocations);
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_
diff --git a/tools/relocation_packer/src/run_length_encoder_unittest.cc b/tools/relocation_packer/src/run_length_encoder_unittest.cc
new file mode 100644
index 0000000..83370f2
--- /dev/null
+++ b/tools/relocation_packer/src/run_length_encoder_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "run_length_encoder.h"
+
+#include <vector>
+#include "elf.h"
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AddRelocation(ELF::Addr addr, std::vector<ELF::Rel>* relocations) {
+ ELF::Rel relocation;
+ relocation.r_offset = addr;
+ relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode);
+ relocations->push_back(relocation);
+}
+
+bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) {
+ return relocation.r_offset == addr &&
+ ELF_R_SYM(relocation.r_info) == 0 &&
+ ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode;
+}
+
+} // namespace
+
+namespace relocation_packer {
+
+TEST(RunLength, Encode) {
+ std::vector<ELF::Rel> relocations;
+ std::vector<ELF::Xword> packed;
+
+ RelocationRunLengthCodec codec;
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(0, packed.size());
+
+ // Add one relocation (insufficient data to encode).
+ AddRelocation(0xf00d0000, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(0, packed.size());
+
+ // Add a second relocation, 4 byte delta (minimum data to encode).
+ AddRelocation(0xf00d0004, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(4, packed.size());
+ // One count-delta pair present.
+ EXPECT_EQ(1, packed[0]);
+ // Initial relocation.
+ EXPECT_EQ(0xf00d0000, packed[1]);
+ // Run of a single relocation, 4 byte delta.
+ EXPECT_EQ(1, packed[2]);
+ EXPECT_EQ(4, packed[3]);
+
+ // Add a third relocation, 4 byte delta.
+ AddRelocation(0xf00d0008, &relocations);
+
+ // Add three more relocations, 8 byte deltas.
+ AddRelocation(0xf00d0010, &relocations);
+ AddRelocation(0xf00d0018, &relocations);
+ AddRelocation(0xf00d0020, &relocations);
+
+ packed.clear();
+ codec.Encode(relocations, &packed);
+
+ EXPECT_EQ(6, packed.size());
+ // Two count-delta pairs present.
+ EXPECT_EQ(2, packed[0]);
+ // Initial relocation.
+ EXPECT_EQ(0xf00d0000, packed[1]);
+ // Run of two relocations, 4 byte deltas.
+ EXPECT_EQ(2, packed[2]);
+ EXPECT_EQ(4, packed[3]);
+ // Run of three relocations, 8 byte deltas.
+ EXPECT_EQ(3, packed[4]);
+ EXPECT_EQ(8, packed[5]);
+}
+
+TEST(RunLength, Decode) {
+ std::vector<ELF::Xword> packed;
+ std::vector<ELF::Rel> relocations;
+
+ RelocationRunLengthCodec codec;
+ codec.Decode(packed, &relocations);
+
+ EXPECT_EQ(0, relocations.size());
+
+ // Two count-delta pairs.
+ packed.push_back(2);
+ // Initial relocation.
+ packed.push_back(0xc0de0000);
+ // Run of two relocations, 4 byte deltas.
+ packed.push_back(2);
+ packed.push_back(4);
+ // Run of three relocations, 8 byte deltas.
+ packed.push_back(3);
+ packed.push_back(8);
+
+ relocations.clear();
+ codec.Decode(packed, &relocations);
+
+ EXPECT_EQ(6, relocations.size());
+ // Initial relocation.
+ EXPECT_TRUE(CheckRelocation(0xc0de0000, relocations[0]));
+ // Two relocations, 4 byte deltas.
+ EXPECT_TRUE(CheckRelocation(0xc0de0004, relocations[1]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0008, relocations[2]));
+ // Three relocations, 8 byte deltas.
+ EXPECT_TRUE(CheckRelocation(0xc0de0010, relocations[3]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0018, relocations[4]));
+ EXPECT_TRUE(CheckRelocation(0xc0de0020, relocations[5]));
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/sleb128.cc b/tools/relocation_packer/src/sleb128.cc
new file mode 100644
index 0000000..a10bd79
--- /dev/null
+++ b/tools/relocation_packer/src/sleb128.cc
@@ -0,0 +1,95 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sleb128.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Empty constructor and destructor to silence chromium-style.
+Sleb128Encoder::Sleb128Encoder() { }
+Sleb128Encoder::~Sleb128Encoder() { }
+
+// Add a single value to the encoding. Values are encoded with variable
+// length. The least significant 7 bits of each byte hold 7 bits of data,
+// and the most significant bit is set on each byte except the last. The
+// value is sign extended up to a multiple of 7 bits (ensuring that the
+// most significant bit is zero for a positive number and one for a
+// negative number).
+void Sleb128Encoder::Enqueue(ELF::Sxword value) {
+ static const size_t size = CHAR_BIT * sizeof(value);
+
+ bool more = true;
+ const bool negative = value < 0;
+
+ while (more) {
+ uint8_t byte = value & 127;
+ value >>= 7;
+
+ // Sign extend if encoding a -ve value.
+ if (negative)
+ value |= -(static_cast<ELF::Sxword>(1) << (size - 7));
+
+ // The sign bit of byte is second high order bit.
+ const bool sign_bit = byte & 64;
+ if ((value == 0 && !sign_bit) || (value == -1 && sign_bit))
+ more = false;
+ else
+ byte |= 128;
+ encoding_.push_back(byte);
+ }
+}
+
+// Add a vector of values to the encoding.
+void Sleb128Encoder::EnqueueAll(const std::vector<ELF::Sxword>& values) {
+ for (size_t i = 0; i < values.size(); ++i)
+ Enqueue(values[i]);
+}
+
+// Create a new decoder for the given encoded stream.
+Sleb128Decoder::Sleb128Decoder(const std::vector<uint8_t>& encoding) {
+ encoding_ = encoding;
+ cursor_ = 0;
+}
+
+// Empty destructor to silence chromium-style.
+Sleb128Decoder::~Sleb128Decoder() { }
+
+// Decode and retrieve a single value from the encoding. Consume bytes
+// until one without its most significant bit is found, and re-form the
+// value from the 7 bit fields of the bytes consumed.
+ELF::Sxword Sleb128Decoder::Dequeue() {
+ ELF::Sxword value = 0;
+ static const size_t size = CHAR_BIT * sizeof(value);
+
+ size_t shift = 0;
+ uint8_t byte;
+
+ // Loop until we reach a byte with its high order bit clear.
+ do {
+ byte = encoding_[cursor_++];
+ value |= (static_cast<ELF::Sxword>(byte & 127) << shift);
+ shift += 7;
+ } while (byte & 128);
+
+ // The sign bit is second high order bit of the final byte decoded.
+ // Sign extend if value is -ve and we did not shift all of it.
+ if (shift < size && (byte & 64))
+ value |= -(static_cast<ELF::Sxword>(1) << shift);
+
+ return value;
+}
+
+// Decode and retrieve all remaining values from the encoding.
+void Sleb128Decoder::DequeueAll(std::vector<ELF::Sxword>* values) {
+ while (cursor_ < encoding_.size())
+ values->push_back(Dequeue());
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/src/sleb128.h b/tools/relocation_packer/src/sleb128.h
new file mode 100644
index 0000000..3544543
--- /dev/null
+++ b/tools/relocation_packer/src/sleb128.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// SLEB128 encoder and decoder for packed relative relocations.
+//
+// Delta encoded relative relocations consist of a large number
+// of pairs signed integer values, many with small values. Encoding these
+// as signed LEB128 saves space.
+//
+// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128.
+
+#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
+#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
+
+#include <stdint.h>
+#include <unistd.h>
+#include <vector>
+
+#include "elf_traits.h"
+
+namespace relocation_packer {
+
+// Encode packed words as a signed LEB128 byte stream.
+class Sleb128Encoder {
+ public:
+ // Explicit (but empty) constructor and destructor, for chromium-style.
+ Sleb128Encoder();
+ ~Sleb128Encoder();
+
+ // Add a value to the encoding stream.
+ // |value| is the signed int to add.
+ void Enqueue(ELF::Sxword value);
+
+ // Add a vector of values to the encoding stream.
+ // |values| is the vector of signed ints to add.
+ void EnqueueAll(const std::vector<ELF::Sxword>& values);
+
+ // Retrieve the encoded representation of the values.
+ // |encoding| is the returned vector of encoded data.
+ void GetEncoding(std::vector<uint8_t>* encoding) { *encoding = encoding_; }
+
+ private:
+ // Growable vector holding the encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+};
+
+// Decode a LEB128 byte stream to produce packed words.
+class Sleb128Decoder {
+ public:
+ // Create a new decoder for the given encoded stream.
+ // |encoding| is the vector of encoded data.
+ explicit Sleb128Decoder(const std::vector<uint8_t>& encoding);
+
+ // Explicit (but empty) destructor, for chromium-style.
+ ~Sleb128Decoder();
+
+ // Retrieve the next value from the encoded stream.
+ ELF::Sxword Dequeue();
+
+ // Retrieve all remaining values from the encoded stream.
+ // |values| is the vector of decoded data.
+ void DequeueAll(std::vector<ELF::Sxword>* values);
+
+ private:
+ // Encoded LEB128 stream.
+ std::vector<uint8_t> encoding_;
+
+ // Cursor indicating the current stream retrieval point.
+ size_t cursor_;
+};
+
+} // namespace relocation_packer
+
+#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_
diff --git a/tools/relocation_packer/src/sleb128_unittest.cc b/tools/relocation_packer/src/sleb128_unittest.cc
new file mode 100644
index 0000000..60a5d0d
--- /dev/null
+++ b/tools/relocation_packer/src/sleb128_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sleb128.h"
+
+#include <vector>
+#include "elf_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace relocation_packer {
+
+TEST(Sleb128, Encoder) {
+ std::vector<ELF::Sxword> values;
+ values.push_back(624485);
+ values.push_back(0);
+ values.push_back(1);
+ values.push_back(63);
+ values.push_back(64);
+ values.push_back(-1);
+ values.push_back(-624485);
+
+ Sleb128Encoder encoder;
+ encoder.EnqueueAll(values);
+
+ encoder.Enqueue(2147483647);
+ encoder.Enqueue(-2147483648);
+ encoder.Enqueue(9223372036854775807ll);
+ encoder.Enqueue(-9223372036854775807ll - 1);
+
+ std::vector<uint8_t> encoding;
+ encoder.GetEncoding(&encoding);
+
+ EXPECT_EQ(42u, encoding.size());
+ // 624485
+ EXPECT_EQ(0xe5, encoding[0]);
+ EXPECT_EQ(0x8e, encoding[1]);
+ EXPECT_EQ(0x26, encoding[2]);
+ // 0
+ EXPECT_EQ(0x00, encoding[3]);
+ // 1
+ EXPECT_EQ(0x01, encoding[4]);
+ // 63
+ EXPECT_EQ(0x3f, encoding[5]);
+ // 64
+ EXPECT_EQ(0xc0, encoding[6]);
+ EXPECT_EQ(0x00, encoding[7]);
+ // -1
+ EXPECT_EQ(0x7f, encoding[8]);
+ // -624485
+ EXPECT_EQ(0x9b, encoding[9]);
+ EXPECT_EQ(0xf1, encoding[10]);
+ EXPECT_EQ(0x59, encoding[11]);
+ // 2147483647
+ EXPECT_EQ(0xff, encoding[12]);
+ EXPECT_EQ(0xff, encoding[13]);
+ EXPECT_EQ(0xff, encoding[14]);
+ EXPECT_EQ(0xff, encoding[15]);
+ EXPECT_EQ(0x07, encoding[16]);
+ // -2147483648
+ EXPECT_EQ(0x80, encoding[17]);
+ EXPECT_EQ(0x80, encoding[18]);
+ EXPECT_EQ(0x80, encoding[19]);
+ EXPECT_EQ(0x80, encoding[20]);
+ EXPECT_EQ(0x78, encoding[21]);
+ // 9223372036854775807
+ EXPECT_EQ(0xff, encoding[22]);
+ EXPECT_EQ(0xff, encoding[23]);
+ EXPECT_EQ(0xff, encoding[24]);
+ EXPECT_EQ(0xff, encoding[25]);
+ EXPECT_EQ(0xff, encoding[26]);
+ EXPECT_EQ(0xff, encoding[27]);
+ EXPECT_EQ(0xff, encoding[28]);
+ EXPECT_EQ(0xff, encoding[29]);
+ EXPECT_EQ(0xff, encoding[30]);
+ EXPECT_EQ(0x00, encoding[31]);
+ // -9223372036854775808
+ EXPECT_EQ(0x80, encoding[32]);
+ EXPECT_EQ(0x80, encoding[33]);
+ EXPECT_EQ(0x80, encoding[34]);
+ EXPECT_EQ(0x80, encoding[35]);
+ EXPECT_EQ(0x80, encoding[36]);
+ EXPECT_EQ(0x80, encoding[37]);
+ EXPECT_EQ(0x80, encoding[38]);
+ EXPECT_EQ(0x80, encoding[39]);
+ EXPECT_EQ(0x80, encoding[40]);
+ EXPECT_EQ(0x7f, encoding[41]);
+}
+
+TEST(Sleb128, Decoder) {
+ std::vector<uint8_t> encoding;
+ // 624485
+ encoding.push_back(0xe5);
+ encoding.push_back(0x8e);
+ encoding.push_back(0x26);
+ // 0
+ encoding.push_back(0x00);
+ // 1
+ encoding.push_back(0x01);
+ // 63
+ encoding.push_back(0x3f);
+ // 64
+ encoding.push_back(0xc0);
+ encoding.push_back(0x00);
+ // -1
+ encoding.push_back(0x7f);
+ // -624485
+ encoding.push_back(0x9b);
+ encoding.push_back(0xf1);
+ encoding.push_back(0x59);
+ // 2147483647
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0x07);
+ // -2147483648
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x78);
+ // 9223372036854775807
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0xff);
+ encoding.push_back(0x00);
+ // -9223372036854775808
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x80);
+ encoding.push_back(0x7f);
+
+ Sleb128Decoder decoder(encoding);
+
+ EXPECT_EQ(624485, decoder.Dequeue());
+
+ std::vector<ELF::Sxword> dequeued;
+ decoder.DequeueAll(&dequeued);
+
+ EXPECT_EQ(10u, dequeued.size());
+ EXPECT_EQ(0, dequeued[0]);
+ EXPECT_EQ(1, dequeued[1]);
+ EXPECT_EQ(63, dequeued[2]);
+ EXPECT_EQ(64, dequeued[3]);
+ EXPECT_EQ(-1, dequeued[4]);
+ EXPECT_EQ(-624485, dequeued[5]);
+ EXPECT_EQ(2147483647, dequeued[6]);
+ EXPECT_EQ(-2147483648, dequeued[7]);
+ EXPECT_EQ(9223372036854775807ll, dequeued[8]);
+ EXPECT_EQ(-9223372036854775807ll - 1, dequeued[9]);
+}
+
+} // namespace relocation_packer
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
new file mode 100644
index 0000000..5e1fa74
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc
@@ -0,0 +1,1014 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test data for packing/unpacking. When compiled, creates a run of
+// relative relocations.
+//
+// See generate_elf_file_unittest_relocs.sh for instructions on how to build
+// unit test data from this source file.
+
+const int i = 0;
+
+// Generator:
+// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i'
+const void* pointer_0 = &i;
+const void* pointer_1 = &i;
+const void* pointer_2 = &i;
+const void* pointer_3 = &i;
+const void* pointer_4 = &i;
+const void* pointer_5 = &i;
+const void* pointer_6 = &i;
+const void* pointer_7 = &i;
+const void* pointer_8 = &i;
+const void* pointer_9 = &i;
+const void* pointer_10 = &i;
+const void* pointer_11 = &i;
+const void* pointer_12 = &i;
+const void* pointer_13 = &i;
+const void* pointer_14 = &i;
+const void* pointer_15 = &i;
+const void* pointer_16 = &i;
+const void* pointer_17 = &i;
+const void* pointer_18 = &i;
+const void* pointer_19 = &i;
+const void* pointer_20 = &i;
+const void* pointer_21 = &i;
+const void* pointer_22 = &i;
+const void* pointer_23 = &i;
+const void* pointer_24 = &i;
+const void* pointer_25 = &i;
+const void* pointer_26 = &i;
+const void* pointer_27 = &i;
+const void* pointer_28 = &i;
+const void* pointer_29 = &i;
+const void* pointer_30 = &i;
+const void* pointer_31 = &i;
+const void* pointer_32 = &i;
+const void* pointer_33 = &i;
+const void* pointer_34 = &i;
+const void* pointer_35 = &i;
+const void* pointer_36 = &i;
+const void* pointer_37 = &i;
+const void* pointer_38 = &i;
+const void* pointer_39 = &i;
+const void* pointer_40 = &i;
+const void* pointer_41 = &i;
+const void* pointer_42 = &i;
+const void* pointer_43 = &i;
+const void* pointer_44 = &i;
+const void* pointer_45 = &i;
+const void* pointer_46 = &i;
+const void* pointer_47 = &i;
+const void* pointer_48 = &i;
+const void* pointer_49 = &i;
+const void* pointer_50 = &i;
+const void* pointer_51 = &i;
+const void* pointer_52 = &i;
+const void* pointer_53 = &i;
+const void* pointer_54 = &i;
+const void* pointer_55 = &i;
+const void* pointer_56 = &i;
+const void* pointer_57 = &i;
+const void* pointer_58 = &i;
+const void* pointer_59 = &i;
+const void* pointer_60 = &i;
+const void* pointer_61 = &i;
+const void* pointer_62 = &i;
+const void* pointer_63 = &i;
+const void* pointer_64 = &i;
+const void* pointer_65 = &i;
+const void* pointer_66 = &i;
+const void* pointer_67 = &i;
+const void* pointer_68 = &i;
+const void* pointer_69 = &i;
+const void* pointer_70 = &i;
+const void* pointer_71 = &i;
+const void* pointer_72 = &i;
+const void* pointer_73 = &i;
+const void* pointer_74 = &i;
+const void* pointer_75 = &i;
+const void* pointer_76 = &i;
+const void* pointer_77 = &i;
+const void* pointer_78 = &i;
+const void* pointer_79 = &i;
+const void* pointer_80 = &i;
+const void* pointer_81 = &i;
+const void* pointer_82 = &i;
+const void* pointer_83 = &i;
+const void* pointer_84 = &i;
+const void* pointer_85 = &i;
+const void* pointer_86 = &i;
+const void* pointer_87 = &i;
+const void* pointer_88 = &i;
+const void* pointer_89 = &i;
+const void* pointer_90 = &i;
+const void* pointer_91 = &i;
+const void* pointer_92 = &i;
+const void* pointer_93 = &i;
+const void* pointer_94 = &i;
+const void* pointer_95 = &i;
+const void* pointer_96 = &i;
+const void* pointer_97 = &i;
+const void* pointer_98 = &i;
+const void* pointer_99 = &i;
+const void* pointer_100 = &i;
+const void* pointer_101 = &i;
+const void* pointer_102 = &i;
+const void* pointer_103 = &i;
+const void* pointer_104 = &i;
+const void* pointer_105 = &i;
+const void* pointer_106 = &i;
+const void* pointer_107 = &i;
+const void* pointer_108 = &i;
+const void* pointer_109 = &i;
+const void* pointer_110 = &i;
+const void* pointer_111 = &i;
+const void* pointer_112 = &i;
+const void* pointer_113 = &i;
+const void* pointer_114 = &i;
+const void* pointer_115 = &i;
+const void* pointer_116 = &i;
+const void* pointer_117 = &i;
+const void* pointer_118 = &i;
+const void* pointer_119 = &i;
+const void* pointer_120 = &i;
+const void* pointer_121 = &i;
+const void* pointer_122 = &i;
+const void* pointer_123 = &i;
+const void* pointer_124 = &i;
+const void* pointer_125 = &i;
+const void* pointer_126 = &i;
+const void* pointer_127 = &i;
+const void* pointer_128 = &i;
+const void* pointer_129 = &i;
+const void* pointer_130 = &i;
+const void* pointer_131 = &i;
+const void* pointer_132 = &i;
+const void* pointer_133 = &i;
+const void* pointer_134 = &i;
+const void* pointer_135 = &i;
+const void* pointer_136 = &i;
+const void* pointer_137 = &i;
+const void* pointer_138 = &i;
+const void* pointer_139 = &i;
+const void* pointer_140 = &i;
+const void* pointer_141 = &i;
+const void* pointer_142 = &i;
+const void* pointer_143 = &i;
+const void* pointer_144 = &i;
+const void* pointer_145 = &i;
+const void* pointer_146 = &i;
+const void* pointer_147 = &i;
+const void* pointer_148 = &i;
+const void* pointer_149 = &i;
+const void* pointer_150 = &i;
+const void* pointer_151 = &i;
+const void* pointer_152 = &i;
+const void* pointer_153 = &i;
+const void* pointer_154 = &i;
+const void* pointer_155 = &i;
+const void* pointer_156 = &i;
+const void* pointer_157 = &i;
+const void* pointer_158 = &i;
+const void* pointer_159 = &i;
+const void* pointer_160 = &i;
+const void* pointer_161 = &i;
+const void* pointer_162 = &i;
+const void* pointer_163 = &i;
+const void* pointer_164 = &i;
+const void* pointer_165 = &i;
+const void* pointer_166 = &i;
+const void* pointer_167 = &i;
+const void* pointer_168 = &i;
+const void* pointer_169 = &i;
+const void* pointer_170 = &i;
+const void* pointer_171 = &i;
+const void* pointer_172 = &i;
+const void* pointer_173 = &i;
+const void* pointer_174 = &i;
+const void* pointer_175 = &i;
+const void* pointer_176 = &i;
+const void* pointer_177 = &i;
+const void* pointer_178 = &i;
+const void* pointer_179 = &i;
+const void* pointer_180 = &i;
+const void* pointer_181 = &i;
+const void* pointer_182 = &i;
+const void* pointer_183 = &i;
+const void* pointer_184 = &i;
+const void* pointer_185 = &i;
+const void* pointer_186 = &i;
+const void* pointer_187 = &i;
+const void* pointer_188 = &i;
+const void* pointer_189 = &i;
+const void* pointer_190 = &i;
+const void* pointer_191 = &i;
+const void* pointer_192 = &i;
+const void* pointer_193 = &i;
+const void* pointer_194 = &i;
+const void* pointer_195 = &i;
+const void* pointer_196 = &i;
+const void* pointer_197 = &i;
+const void* pointer_198 = &i;
+const void* pointer_199 = &i;
+const void* pointer_200 = &i;
+const void* pointer_201 = &i;
+const void* pointer_202 = &i;
+const void* pointer_203 = &i;
+const void* pointer_204 = &i;
+const void* pointer_205 = &i;
+const void* pointer_206 = &i;
+const void* pointer_207 = &i;
+const void* pointer_208 = &i;
+const void* pointer_209 = &i;
+const void* pointer_210 = &i;
+const void* pointer_211 = &i;
+const void* pointer_212 = &i;
+const void* pointer_213 = &i;
+const void* pointer_214 = &i;
+const void* pointer_215 = &i;
+const void* pointer_216 = &i;
+const void* pointer_217 = &i;
+const void* pointer_218 = &i;
+const void* pointer_219 = &i;
+const void* pointer_220 = &i;
+const void* pointer_221 = &i;
+const void* pointer_222 = &i;
+const void* pointer_223 = &i;
+const void* pointer_224 = &i;
+const void* pointer_225 = &i;
+const void* pointer_226 = &i;
+const void* pointer_227 = &i;
+const void* pointer_228 = &i;
+const void* pointer_229 = &i;
+const void* pointer_230 = &i;
+const void* pointer_231 = &i;
+const void* pointer_232 = &i;
+const void* pointer_233 = &i;
+const void* pointer_234 = &i;
+const void* pointer_235 = &i;
+const void* pointer_236 = &i;
+const void* pointer_237 = &i;
+const void* pointer_238 = &i;
+const void* pointer_239 = &i;
+const void* pointer_240 = &i;
+const void* pointer_241 = &i;
+const void* pointer_242 = &i;
+const void* pointer_243 = &i;
+const void* pointer_244 = &i;
+const void* pointer_245 = &i;
+const void* pointer_246 = &i;
+const void* pointer_247 = &i;
+const void* pointer_248 = &i;
+const void* pointer_249 = &i;
+const void* pointer_250 = &i;
+const void* pointer_251 = &i;
+const void* pointer_252 = &i;
+const void* pointer_253 = &i;
+const void* pointer_254 = &i;
+const void* pointer_255 = &i;
+const void* pointer_256 = &i;
+const void* pointer_257 = &i;
+const void* pointer_258 = &i;
+const void* pointer_259 = &i;
+const void* pointer_260 = &i;
+const void* pointer_261 = &i;
+const void* pointer_262 = &i;
+const void* pointer_263 = &i;
+const void* pointer_264 = &i;
+const void* pointer_265 = &i;
+const void* pointer_266 = &i;
+const void* pointer_267 = &i;
+const void* pointer_268 = &i;
+const void* pointer_269 = &i;
+const void* pointer_270 = &i;
+const void* pointer_271 = &i;
+const void* pointer_272 = &i;
+const void* pointer_273 = &i;
+const void* pointer_274 = &i;
+const void* pointer_275 = &i;
+const void* pointer_276 = &i;
+const void* pointer_277 = &i;
+const void* pointer_278 = &i;
+const void* pointer_279 = &i;
+const void* pointer_280 = &i;
+const void* pointer_281 = &i;
+const void* pointer_282 = &i;
+const void* pointer_283 = &i;
+const void* pointer_284 = &i;
+const void* pointer_285 = &i;
+const void* pointer_286 = &i;
+const void* pointer_287 = &i;
+const void* pointer_288 = &i;
+const void* pointer_289 = &i;
+const void* pointer_290 = &i;
+const void* pointer_291 = &i;
+const void* pointer_292 = &i;
+const void* pointer_293 = &i;
+const void* pointer_294 = &i;
+const void* pointer_295 = &i;
+const void* pointer_296 = &i;
+const void* pointer_297 = &i;
+const void* pointer_298 = &i;
+const void* pointer_299 = &i;
+const void* pointer_300 = &i;
+const void* pointer_301 = &i;
+const void* pointer_302 = &i;
+const void* pointer_303 = &i;
+const void* pointer_304 = &i;
+const void* pointer_305 = &i;
+const void* pointer_306 = &i;
+const void* pointer_307 = &i;
+const void* pointer_308 = &i;
+const void* pointer_309 = &i;
+const void* pointer_310 = &i;
+const void* pointer_311 = &i;
+const void* pointer_312 = &i;
+const void* pointer_313 = &i;
+const void* pointer_314 = &i;
+const void* pointer_315 = &i;
+const void* pointer_316 = &i;
+const void* pointer_317 = &i;
+const void* pointer_318 = &i;
+const void* pointer_319 = &i;
+const void* pointer_320 = &i;
+const void* pointer_321 = &i;
+const void* pointer_322 = &i;
+const void* pointer_323 = &i;
+const void* pointer_324 = &i;
+const void* pointer_325 = &i;
+const void* pointer_326 = &i;
+const void* pointer_327 = &i;
+const void* pointer_328 = &i;
+const void* pointer_329 = &i;
+const void* pointer_330 = &i;
+const void* pointer_331 = &i;
+const void* pointer_332 = &i;
+const void* pointer_333 = &i;
+const void* pointer_334 = &i;
+const void* pointer_335 = &i;
+const void* pointer_336 = &i;
+const void* pointer_337 = &i;
+const void* pointer_338 = &i;
+const void* pointer_339 = &i;
+const void* pointer_340 = &i;
+const void* pointer_341 = &i;
+const void* pointer_342 = &i;
+const void* pointer_343 = &i;
+const void* pointer_344 = &i;
+const void* pointer_345 = &i;
+const void* pointer_346 = &i;
+const void* pointer_347 = &i;
+const void* pointer_348 = &i;
+const void* pointer_349 = &i;
+const void* pointer_350 = &i;
+const void* pointer_351 = &i;
+const void* pointer_352 = &i;
+const void* pointer_353 = &i;
+const void* pointer_354 = &i;
+const void* pointer_355 = &i;
+const void* pointer_356 = &i;
+const void* pointer_357 = &i;
+const void* pointer_358 = &i;
+const void* pointer_359 = &i;
+const void* pointer_360 = &i;
+const void* pointer_361 = &i;
+const void* pointer_362 = &i;
+const void* pointer_363 = &i;
+const void* pointer_364 = &i;
+const void* pointer_365 = &i;
+const void* pointer_366 = &i;
+const void* pointer_367 = &i;
+const void* pointer_368 = &i;
+const void* pointer_369 = &i;
+const void* pointer_370 = &i;
+const void* pointer_371 = &i;
+const void* pointer_372 = &i;
+const void* pointer_373 = &i;
+const void* pointer_374 = &i;
+const void* pointer_375 = &i;
+const void* pointer_376 = &i;
+const void* pointer_377 = &i;
+const void* pointer_378 = &i;
+const void* pointer_379 = &i;
+const void* pointer_380 = &i;
+const void* pointer_381 = &i;
+const void* pointer_382 = &i;
+const void* pointer_383 = &i;
+const void* pointer_384 = &i;
+const void* pointer_385 = &i;
+const void* pointer_386 = &i;
+const void* pointer_387 = &i;
+const void* pointer_388 = &i;
+const void* pointer_389 = &i;
+const void* pointer_390 = &i;
+const void* pointer_391 = &i;
+const void* pointer_392 = &i;
+const void* pointer_393 = &i;
+const void* pointer_394 = &i;
+const void* pointer_395 = &i;
+const void* pointer_396 = &i;
+const void* pointer_397 = &i;
+const void* pointer_398 = &i;
+const void* pointer_399 = &i;
+const void* pointer_400 = &i;
+const void* pointer_401 = &i;
+const void* pointer_402 = &i;
+const void* pointer_403 = &i;
+const void* pointer_404 = &i;
+const void* pointer_405 = &i;
+const void* pointer_406 = &i;
+const void* pointer_407 = &i;
+const void* pointer_408 = &i;
+const void* pointer_409 = &i;
+const void* pointer_410 = &i;
+const void* pointer_411 = &i;
+const void* pointer_412 = &i;
+const void* pointer_413 = &i;
+const void* pointer_414 = &i;
+const void* pointer_415 = &i;
+const void* pointer_416 = &i;
+const void* pointer_417 = &i;
+const void* pointer_418 = &i;
+const void* pointer_419 = &i;
+const void* pointer_420 = &i;
+const void* pointer_421 = &i;
+const void* pointer_422 = &i;
+const void* pointer_423 = &i;
+const void* pointer_424 = &i;
+const void* pointer_425 = &i;
+const void* pointer_426 = &i;
+const void* pointer_427 = &i;
+const void* pointer_428 = &i;
+const void* pointer_429 = &i;
+const void* pointer_430 = &i;
+const void* pointer_431 = &i;
+const void* pointer_432 = &i;
+const void* pointer_433 = &i;
+const void* pointer_434 = &i;
+const void* pointer_435 = &i;
+const void* pointer_436 = &i;
+const void* pointer_437 = &i;
+const void* pointer_438 = &i;
+const void* pointer_439 = &i;
+const void* pointer_440 = &i;
+const void* pointer_441 = &i;
+const void* pointer_442 = &i;
+const void* pointer_443 = &i;
+const void* pointer_444 = &i;
+const void* pointer_445 = &i;
+const void* pointer_446 = &i;
+const void* pointer_447 = &i;
+const void* pointer_448 = &i;
+const void* pointer_449 = &i;
+const void* pointer_450 = &i;
+const void* pointer_451 = &i;
+const void* pointer_452 = &i;
+const void* pointer_453 = &i;
+const void* pointer_454 = &i;
+const void* pointer_455 = &i;
+const void* pointer_456 = &i;
+const void* pointer_457 = &i;
+const void* pointer_458 = &i;
+const void* pointer_459 = &i;
+const void* pointer_460 = &i;
+const void* pointer_461 = &i;
+const void* pointer_462 = &i;
+const void* pointer_463 = &i;
+const void* pointer_464 = &i;
+const void* pointer_465 = &i;
+const void* pointer_466 = &i;
+const void* pointer_467 = &i;
+const void* pointer_468 = &i;
+const void* pointer_469 = &i;
+const void* pointer_470 = &i;
+const void* pointer_471 = &i;
+const void* pointer_472 = &i;
+const void* pointer_473 = &i;
+const void* pointer_474 = &i;
+const void* pointer_475 = &i;
+const void* pointer_476 = &i;
+const void* pointer_477 = &i;
+const void* pointer_478 = &i;
+const void* pointer_479 = &i;
+const void* pointer_480 = &i;
+const void* pointer_481 = &i;
+const void* pointer_482 = &i;
+const void* pointer_483 = &i;
+const void* pointer_484 = &i;
+const void* pointer_485 = &i;
+const void* pointer_486 = &i;
+const void* pointer_487 = &i;
+const void* pointer_488 = &i;
+const void* pointer_489 = &i;
+const void* pointer_490 = &i;
+const void* pointer_491 = &i;
+const void* pointer_492 = &i;
+const void* pointer_493 = &i;
+const void* pointer_494 = &i;
+const void* pointer_495 = &i;
+const void* pointer_496 = &i;
+const void* pointer_497 = &i;
+const void* pointer_498 = &i;
+const void* pointer_499 = &i;
+const void* pointer_500 = &i;
+const void* pointer_501 = &i;
+const void* pointer_502 = &i;
+const void* pointer_503 = &i;
+const void* pointer_504 = &i;
+const void* pointer_505 = &i;
+const void* pointer_506 = &i;
+const void* pointer_507 = &i;
+const void* pointer_508 = &i;
+const void* pointer_509 = &i;
+const void* pointer_510 = &i;
+const void* pointer_511 = &i;
+const void* pointer_512 = &i;
+const void* pointer_513 = &i;
+const void* pointer_514 = &i;
+const void* pointer_515 = &i;
+const void* pointer_516 = &i;
+const void* pointer_517 = &i;
+const void* pointer_518 = &i;
+const void* pointer_519 = &i;
+const void* pointer_520 = &i;
+const void* pointer_521 = &i;
+const void* pointer_522 = &i;
+const void* pointer_523 = &i;
+const void* pointer_524 = &i;
+const void* pointer_525 = &i;
+const void* pointer_526 = &i;
+const void* pointer_527 = &i;
+const void* pointer_528 = &i;
+const void* pointer_529 = &i;
+const void* pointer_530 = &i;
+const void* pointer_531 = &i;
+const void* pointer_532 = &i;
+const void* pointer_533 = &i;
+const void* pointer_534 = &i;
+const void* pointer_535 = &i;
+const void* pointer_536 = &i;
+const void* pointer_537 = &i;
+const void* pointer_538 = &i;
+const void* pointer_539 = &i;
+const void* pointer_540 = &i;
+const void* pointer_541 = &i;
+const void* pointer_542 = &i;
+const void* pointer_543 = &i;
+const void* pointer_544 = &i;
+const void* pointer_545 = &i;
+const void* pointer_546 = &i;
+const void* pointer_547 = &i;
+const void* pointer_548 = &i;
+const void* pointer_549 = &i;
+const void* pointer_550 = &i;
+const void* pointer_551 = &i;
+const void* pointer_552 = &i;
+const void* pointer_553 = &i;
+const void* pointer_554 = &i;
+const void* pointer_555 = &i;
+const void* pointer_556 = &i;
+const void* pointer_557 = &i;
+const void* pointer_558 = &i;
+const void* pointer_559 = &i;
+const void* pointer_560 = &i;
+const void* pointer_561 = &i;
+const void* pointer_562 = &i;
+const void* pointer_563 = &i;
+const void* pointer_564 = &i;
+const void* pointer_565 = &i;
+const void* pointer_566 = &i;
+const void* pointer_567 = &i;
+const void* pointer_568 = &i;
+const void* pointer_569 = &i;
+const void* pointer_570 = &i;
+const void* pointer_571 = &i;
+const void* pointer_572 = &i;
+const void* pointer_573 = &i;
+const void* pointer_574 = &i;
+const void* pointer_575 = &i;
+const void* pointer_576 = &i;
+const void* pointer_577 = &i;
+const void* pointer_578 = &i;
+const void* pointer_579 = &i;
+const void* pointer_580 = &i;
+const void* pointer_581 = &i;
+const void* pointer_582 = &i;
+const void* pointer_583 = &i;
+const void* pointer_584 = &i;
+const void* pointer_585 = &i;
+const void* pointer_586 = &i;
+const void* pointer_587 = &i;
+const void* pointer_588 = &i;
+const void* pointer_589 = &i;
+const void* pointer_590 = &i;
+const void* pointer_591 = &i;
+const void* pointer_592 = &i;
+const void* pointer_593 = &i;
+const void* pointer_594 = &i;
+const void* pointer_595 = &i;
+const void* pointer_596 = &i;
+const void* pointer_597 = &i;
+const void* pointer_598 = &i;
+const void* pointer_599 = &i;
+const void* pointer_600 = &i;
+const void* pointer_601 = &i;
+const void* pointer_602 = &i;
+const void* pointer_603 = &i;
+const void* pointer_604 = &i;
+const void* pointer_605 = &i;
+const void* pointer_606 = &i;
+const void* pointer_607 = &i;
+const void* pointer_608 = &i;
+const void* pointer_609 = &i;
+const void* pointer_610 = &i;
+const void* pointer_611 = &i;
+const void* pointer_612 = &i;
+const void* pointer_613 = &i;
+const void* pointer_614 = &i;
+const void* pointer_615 = &i;
+const void* pointer_616 = &i;
+const void* pointer_617 = &i;
+const void* pointer_618 = &i;
+const void* pointer_619 = &i;
+const void* pointer_620 = &i;
+const void* pointer_621 = &i;
+const void* pointer_622 = &i;
+const void* pointer_623 = &i;
+const void* pointer_624 = &i;
+const void* pointer_625 = &i;
+const void* pointer_626 = &i;
+const void* pointer_627 = &i;
+const void* pointer_628 = &i;
+const void* pointer_629 = &i;
+const void* pointer_630 = &i;
+const void* pointer_631 = &i;
+const void* pointer_632 = &i;
+const void* pointer_633 = &i;
+const void* pointer_634 = &i;
+const void* pointer_635 = &i;
+const void* pointer_636 = &i;
+const void* pointer_637 = &i;
+const void* pointer_638 = &i;
+const void* pointer_639 = &i;
+const void* pointer_640 = &i;
+const void* pointer_641 = &i;
+const void* pointer_642 = &i;
+const void* pointer_643 = &i;
+const void* pointer_644 = &i;
+const void* pointer_645 = &i;
+const void* pointer_646 = &i;
+const void* pointer_647 = &i;
+const void* pointer_648 = &i;
+const void* pointer_649 = &i;
+const void* pointer_650 = &i;
+const void* pointer_651 = &i;
+const void* pointer_652 = &i;
+const void* pointer_653 = &i;
+const void* pointer_654 = &i;
+const void* pointer_655 = &i;
+const void* pointer_656 = &i;
+const void* pointer_657 = &i;
+const void* pointer_658 = &i;
+const void* pointer_659 = &i;
+const void* pointer_660 = &i;
+const void* pointer_661 = &i;
+const void* pointer_662 = &i;
+const void* pointer_663 = &i;
+const void* pointer_664 = &i;
+const void* pointer_665 = &i;
+const void* pointer_666 = &i;
+const void* pointer_667 = &i;
+const void* pointer_668 = &i;
+const void* pointer_669 = &i;
+const void* pointer_670 = &i;
+const void* pointer_671 = &i;
+const void* pointer_672 = &i;
+const void* pointer_673 = &i;
+const void* pointer_674 = &i;
+const void* pointer_675 = &i;
+const void* pointer_676 = &i;
+const void* pointer_677 = &i;
+const void* pointer_678 = &i;
+const void* pointer_679 = &i;
+const void* pointer_680 = &i;
+const void* pointer_681 = &i;
+const void* pointer_682 = &i;
+const void* pointer_683 = &i;
+const void* pointer_684 = &i;
+const void* pointer_685 = &i;
+const void* pointer_686 = &i;
+const void* pointer_687 = &i;
+const void* pointer_688 = &i;
+const void* pointer_689 = &i;
+const void* pointer_690 = &i;
+const void* pointer_691 = &i;
+const void* pointer_692 = &i;
+const void* pointer_693 = &i;
+const void* pointer_694 = &i;
+const void* pointer_695 = &i;
+const void* pointer_696 = &i;
+const void* pointer_697 = &i;
+const void* pointer_698 = &i;
+const void* pointer_699 = &i;
+const void* pointer_700 = &i;
+const void* pointer_701 = &i;
+const void* pointer_702 = &i;
+const void* pointer_703 = &i;
+const void* pointer_704 = &i;
+const void* pointer_705 = &i;
+const void* pointer_706 = &i;
+const void* pointer_707 = &i;
+const void* pointer_708 = &i;
+const void* pointer_709 = &i;
+const void* pointer_710 = &i;
+const void* pointer_711 = &i;
+const void* pointer_712 = &i;
+const void* pointer_713 = &i;
+const void* pointer_714 = &i;
+const void* pointer_715 = &i;
+const void* pointer_716 = &i;
+const void* pointer_717 = &i;
+const void* pointer_718 = &i;
+const void* pointer_719 = &i;
+const void* pointer_720 = &i;
+const void* pointer_721 = &i;
+const void* pointer_722 = &i;
+const void* pointer_723 = &i;
+const void* pointer_724 = &i;
+const void* pointer_725 = &i;
+const void* pointer_726 = &i;
+const void* pointer_727 = &i;
+const void* pointer_728 = &i;
+const void* pointer_729 = &i;
+const void* pointer_730 = &i;
+const void* pointer_731 = &i;
+const void* pointer_732 = &i;
+const void* pointer_733 = &i;
+const void* pointer_734 = &i;
+const void* pointer_735 = &i;
+const void* pointer_736 = &i;
+const void* pointer_737 = &i;
+const void* pointer_738 = &i;
+const void* pointer_739 = &i;
+const void* pointer_740 = &i;
+const void* pointer_741 = &i;
+const void* pointer_742 = &i;
+const void* pointer_743 = &i;
+const void* pointer_744 = &i;
+const void* pointer_745 = &i;
+const void* pointer_746 = &i;
+const void* pointer_747 = &i;
+const void* pointer_748 = &i;
+const void* pointer_749 = &i;
+const void* pointer_750 = &i;
+const void* pointer_751 = &i;
+const void* pointer_752 = &i;
+const void* pointer_753 = &i;
+const void* pointer_754 = &i;
+const void* pointer_755 = &i;
+const void* pointer_756 = &i;
+const void* pointer_757 = &i;
+const void* pointer_758 = &i;
+const void* pointer_759 = &i;
+const void* pointer_760 = &i;
+const void* pointer_761 = &i;
+const void* pointer_762 = &i;
+const void* pointer_763 = &i;
+const void* pointer_764 = &i;
+const void* pointer_765 = &i;
+const void* pointer_766 = &i;
+const void* pointer_767 = &i;
+const void* pointer_768 = &i;
+const void* pointer_769 = &i;
+const void* pointer_770 = &i;
+const void* pointer_771 = &i;
+const void* pointer_772 = &i;
+const void* pointer_773 = &i;
+const void* pointer_774 = &i;
+const void* pointer_775 = &i;
+const void* pointer_776 = &i;
+const void* pointer_777 = &i;
+const void* pointer_778 = &i;
+const void* pointer_779 = &i;
+const void* pointer_780 = &i;
+const void* pointer_781 = &i;
+const void* pointer_782 = &i;
+const void* pointer_783 = &i;
+const void* pointer_784 = &i;
+const void* pointer_785 = &i;
+const void* pointer_786 = &i;
+const void* pointer_787 = &i;
+const void* pointer_788 = &i;
+const void* pointer_789 = &i;
+const void* pointer_790 = &i;
+const void* pointer_791 = &i;
+const void* pointer_792 = &i;
+const void* pointer_793 = &i;
+const void* pointer_794 = &i;
+const void* pointer_795 = &i;
+const void* pointer_796 = &i;
+const void* pointer_797 = &i;
+const void* pointer_798 = &i;
+const void* pointer_799 = &i;
+const void* pointer_800 = &i;
+const void* pointer_801 = &i;
+const void* pointer_802 = &i;
+const void* pointer_803 = &i;
+const void* pointer_804 = &i;
+const void* pointer_805 = &i;
+const void* pointer_806 = &i;
+const void* pointer_807 = &i;
+const void* pointer_808 = &i;
+const void* pointer_809 = &i;
+const void* pointer_810 = &i;
+const void* pointer_811 = &i;
+const void* pointer_812 = &i;
+const void* pointer_813 = &i;
+const void* pointer_814 = &i;
+const void* pointer_815 = &i;
+const void* pointer_816 = &i;
+const void* pointer_817 = &i;
+const void* pointer_818 = &i;
+const void* pointer_819 = &i;
+const void* pointer_820 = &i;
+const void* pointer_821 = &i;
+const void* pointer_822 = &i;
+const void* pointer_823 = &i;
+const void* pointer_824 = &i;
+const void* pointer_825 = &i;
+const void* pointer_826 = &i;
+const void* pointer_827 = &i;
+const void* pointer_828 = &i;
+const void* pointer_829 = &i;
+const void* pointer_830 = &i;
+const void* pointer_831 = &i;
+const void* pointer_832 = &i;
+const void* pointer_833 = &i;
+const void* pointer_834 = &i;
+const void* pointer_835 = &i;
+const void* pointer_836 = &i;
+const void* pointer_837 = &i;
+const void* pointer_838 = &i;
+const void* pointer_839 = &i;
+const void* pointer_840 = &i;
+const void* pointer_841 = &i;
+const void* pointer_842 = &i;
+const void* pointer_843 = &i;
+const void* pointer_844 = &i;
+const void* pointer_845 = &i;
+const void* pointer_846 = &i;
+const void* pointer_847 = &i;
+const void* pointer_848 = &i;
+const void* pointer_849 = &i;
+const void* pointer_850 = &i;
+const void* pointer_851 = &i;
+const void* pointer_852 = &i;
+const void* pointer_853 = &i;
+const void* pointer_854 = &i;
+const void* pointer_855 = &i;
+const void* pointer_856 = &i;
+const void* pointer_857 = &i;
+const void* pointer_858 = &i;
+const void* pointer_859 = &i;
+const void* pointer_860 = &i;
+const void* pointer_861 = &i;
+const void* pointer_862 = &i;
+const void* pointer_863 = &i;
+const void* pointer_864 = &i;
+const void* pointer_865 = &i;
+const void* pointer_866 = &i;
+const void* pointer_867 = &i;
+const void* pointer_868 = &i;
+const void* pointer_869 = &i;
+const void* pointer_870 = &i;
+const void* pointer_871 = &i;
+const void* pointer_872 = &i;
+const void* pointer_873 = &i;
+const void* pointer_874 = &i;
+const void* pointer_875 = &i;
+const void* pointer_876 = &i;
+const void* pointer_877 = &i;
+const void* pointer_878 = &i;
+const void* pointer_879 = &i;
+const void* pointer_880 = &i;
+const void* pointer_881 = &i;
+const void* pointer_882 = &i;
+const void* pointer_883 = &i;
+const void* pointer_884 = &i;
+const void* pointer_885 = &i;
+const void* pointer_886 = &i;
+const void* pointer_887 = &i;
+const void* pointer_888 = &i;
+const void* pointer_889 = &i;
+const void* pointer_890 = &i;
+const void* pointer_891 = &i;
+const void* pointer_892 = &i;
+const void* pointer_893 = &i;
+const void* pointer_894 = &i;
+const void* pointer_895 = &i;
+const void* pointer_896 = &i;
+const void* pointer_897 = &i;
+const void* pointer_898 = &i;
+const void* pointer_899 = &i;
+const void* pointer_900 = &i;
+const void* pointer_901 = &i;
+const void* pointer_902 = &i;
+const void* pointer_903 = &i;
+const void* pointer_904 = &i;
+const void* pointer_905 = &i;
+const void* pointer_906 = &i;
+const void* pointer_907 = &i;
+const void* pointer_908 = &i;
+const void* pointer_909 = &i;
+const void* pointer_910 = &i;
+const void* pointer_911 = &i;
+const void* pointer_912 = &i;
+const void* pointer_913 = &i;
+const void* pointer_914 = &i;
+const void* pointer_915 = &i;
+const void* pointer_916 = &i;
+const void* pointer_917 = &i;
+const void* pointer_918 = &i;
+const void* pointer_919 = &i;
+const void* pointer_920 = &i;
+const void* pointer_921 = &i;
+const void* pointer_922 = &i;
+const void* pointer_923 = &i;
+const void* pointer_924 = &i;
+const void* pointer_925 = &i;
+const void* pointer_926 = &i;
+const void* pointer_927 = &i;
+const void* pointer_928 = &i;
+const void* pointer_929 = &i;
+const void* pointer_930 = &i;
+const void* pointer_931 = &i;
+const void* pointer_932 = &i;
+const void* pointer_933 = &i;
+const void* pointer_934 = &i;
+const void* pointer_935 = &i;
+const void* pointer_936 = &i;
+const void* pointer_937 = &i;
+const void* pointer_938 = &i;
+const void* pointer_939 = &i;
+const void* pointer_940 = &i;
+const void* pointer_941 = &i;
+const void* pointer_942 = &i;
+const void* pointer_943 = &i;
+const void* pointer_944 = &i;
+const void* pointer_945 = &i;
+const void* pointer_946 = &i;
+const void* pointer_947 = &i;
+const void* pointer_948 = &i;
+const void* pointer_949 = &i;
+const void* pointer_950 = &i;
+const void* pointer_951 = &i;
+const void* pointer_952 = &i;
+const void* pointer_953 = &i;
+const void* pointer_954 = &i;
+const void* pointer_955 = &i;
+const void* pointer_956 = &i;
+const void* pointer_957 = &i;
+const void* pointer_958 = &i;
+const void* pointer_959 = &i;
+const void* pointer_960 = &i;
+const void* pointer_961 = &i;
+const void* pointer_962 = &i;
+const void* pointer_963 = &i;
+const void* pointer_964 = &i;
+const void* pointer_965 = &i;
+const void* pointer_966 = &i;
+const void* pointer_967 = &i;
+const void* pointer_968 = &i;
+const void* pointer_969 = &i;
+const void* pointer_970 = &i;
+const void* pointer_971 = &i;
+const void* pointer_972 = &i;
+const void* pointer_973 = &i;
+const void* pointer_974 = &i;
+const void* pointer_975 = &i;
+const void* pointer_976 = &i;
+const void* pointer_977 = &i;
+const void* pointer_978 = &i;
+const void* pointer_979 = &i;
+const void* pointer_980 = &i;
+const void* pointer_981 = &i;
+const void* pointer_982 = &i;
+const void* pointer_983 = &i;
+const void* pointer_984 = &i;
+const void* pointer_985 = &i;
+const void* pointer_986 = &i;
+const void* pointer_987 = &i;
+const void* pointer_988 = &i;
+const void* pointer_989 = &i;
+const void* pointer_990 = &i;
+const void* pointer_991 = &i;
+const void* pointer_992 = &i;
+const void* pointer_993 = &i;
+const void* pointer_994 = &i;
+const void* pointer_995 = &i;
+const void* pointer_996 = &i;
+const void* pointer_997 = &i;
+const void* pointer_998 = &i;
+const void* pointer_999 = &i;
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
new file mode 100755
index 0000000..6ce6d0c
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
new file mode 100755
index 0000000..7cfdd60
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm32_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
new file mode 100755
index 0000000..945b450
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
new file mode 100755
index 0000000..532beac
--- /dev/null
+++ b/tools/relocation_packer/test_data/elf_file_unittest_relocs_arm64_packed.so
Binary files differ
diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
new file mode 100755
index 0000000..e71b5cb
--- /dev/null
+++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Build relocation packer unit test data.
+
+Uses a built relocation packer to generate 'golden' reference test data
+files for elf_file_unittests.cc.
+"""
+
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+def PackArmLibraryRelocations(android_pack_relocations,
+ android_objcopy,
+ added_section,
+ input_path,
+ output_path):
+ # Copy and add a 'NULL' .android.rel.dyn section for the packing tool.
+ with tempfile.NamedTemporaryFile() as stream:
+ stream.write('NULL')
+ stream.flush()
+ objcopy_command = [android_objcopy,
+ '--add-section', '%s=%s' % (added_section, stream.name),
+ input_path, output_path]
+ subprocess.check_call(objcopy_command)
+
+ # Pack relocations.
+ pack_command = [android_pack_relocations, output_path]
+ subprocess.check_call(pack_command)
+
+
+def UnpackArmLibraryRelocations(android_pack_relocations,
+ input_path,
+ output_path):
+ shutil.copy(input_path, output_path)
+
+ # Unpack relocations. We leave the .android.rel.dyn or .android.rela.dyn
+ # in place.
+ unpack_command = [android_pack_relocations, '-u', output_path]
+ subprocess.check_call(unpack_command)
+
+
+def main():
+ parser = optparse.OptionParser()
+
+ parser.add_option('--android-pack-relocations',
+ help='Path to the ARM relocations packer binary')
+ parser.add_option('--android-objcopy',
+ help='Path to the toolchain\'s objcopy binary')
+ parser.add_option('--added-section',
+ choices=['.android.rel.dyn', '.android.rela.dyn'],
+ help='Section to add, one of ".android.rel.dyn" or ".android.rela.dyn"')
+ parser.add_option('--test-file',
+ help='Path to the input test file, an unpacked ARM .so')
+ parser.add_option('--unpacked-output',
+ help='Path to the output file for reference unpacked data')
+ parser.add_option('--packed-output',
+ help='Path to the output file for reference packed data')
+
+ options, _ = parser.parse_args()
+
+ for output in [options.unpacked_output, options.packed_output]:
+ directory = os.path.dirname(output)
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ PackArmLibraryRelocations(options.android_pack_relocations,
+ options.android_objcopy,
+ options.added_section,
+ options.test_file,
+ options.packed_output)
+
+ UnpackArmLibraryRelocations(options.android_pack_relocations,
+ options.packed_output,
+ options.unpacked_output)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
new file mode 100755
index 0000000..f90a2f6
--- /dev/null
+++ b/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Generates elf_file_unittest_relocs_arm{32,64}{,_packed}.so test data files
+# from elf_file_unittest_relocs.cc. Run once to create these test data
+# files; the files are checked into the source tree.
+#
+# To use:
+# ./generate_elf_file_unittest_relocs.sh
+# git add elf_file_unittest_relocs_arm{32,64}{,_packed}.so
+
+function main() {
+ local '-r' test_data_directory="$(pwd)"
+ cd '../../..'
+
+ source tools/cr/cr-bash-helpers.sh
+ local arch
+ for arch in 'arm32' 'arm64'; do
+ cr 'init' '--platform=android' '--type=Debug' '--architecture='"${arch}"
+ cr 'build' 'relocation_packer_unittests_test_data'
+ done
+
+ local '-r' packer='out_android/Debug/obj/tools/relocation_packer'
+ local '-r' gen="${packer}/relocation_packer_unittests_test_data.gen"
+
+ cp "${gen}/elf_file_unittest_relocs_arm"{32,64}{,_packed}'.so' \
+ "${test_data_directory}"
+
+ return 0
+}
+
+main
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index b135cac..790b38b 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -868,6 +868,13 @@
'gl_versions': ['es3'],
'extensions': ['GL_NV_path_rendering'] }],
'arguments': 'GLenum matrixMode' },
+ { 'return_type': 'void',
+ 'known_as': 'glBlendBarrierKHR',
+ 'versions': [{ 'name': 'glBlendBarrierNV',
+ 'extensions': ['GL_NV_blend_equation_advanced'] },
+ { 'name': 'glBlendBarrierKHR',
+ 'extensions': ['GL_KHR_blend_equation_advanced'] }],
+ 'arguments': 'void' },
]
OSMESA_FUNCTIONS = [
@@ -1079,7 +1086,7 @@
'EGLuint64CHROMIUM* sbc', },
{ 'return_type': 'EGLint',
'versions': [{ 'name': 'eglWaitSyncKHR',
- 'extensions': ['EGL_KHR_fence_sync'] }],
+ 'extensions': ['EGL_KHR_fence_sync', 'EGL_KHR_wait_sync'] }],
'arguments': 'EGLDisplay dpy, EGLSyncKHR sync, EGLint flags' }
]
diff --git a/ui/gl/gl_bindings.h b/ui/gl/gl_bindings.h
index 22774f5..aef80f0 100644
--- a/ui/gl/gl_bindings.h
+++ b/ui/gl/gl_bindings.h
@@ -224,6 +224,30 @@
#define GL_PATH_PROJECTION_CHROMIUM 0x1701
#endif
+#ifndef GL_KHR_blend_equation_advanced
+#define GL_KHR_blend_equation_advanced 1
+#define GL_COLORBURN_KHR 0x929A
+#define GL_COLORDODGE_KHR 0x9299
+#define GL_DARKEN_KHR 0x9297
+#define GL_DIFFERENCE_KHR 0x929E
+#define GL_EXCLUSION_KHR 0x92A0
+#define GL_HARDLIGHT_KHR 0x929B
+#define GL_HSL_COLOR_KHR 0x92AF
+#define GL_HSL_HUE_KHR 0x92AD
+#define GL_HSL_LUMINOSITY_KHR 0x92B0
+#define GL_HSL_SATURATION_KHR 0x92AE
+#define GL_LIGHTEN_KHR 0x9298
+#define GL_MULTIPLY_KHR 0x9294
+#define GL_OVERLAY_KHR 0x9296
+#define GL_SCREEN_KHR 0x9295
+#define GL_SOFTLIGHT_KHR 0x929C
+#endif /* GL_KHR_blend_equation_advanced */
+
+#ifndef GL_KHR_blend_equation_advanced_coherent
+#define GL_KHR_blend_equation_advanced_coherent 1
+#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285
+#endif /* GL_KHR_blend_equation_advanced_coherent */
+
#ifndef GL_EXT_disjoint_timer_query
#define GL_EXT_disjoint_timer_query 1
#define GL_QUERY_COUNTER_BITS_EXT 0x8864
diff --git a/ui/gl/gl_fence_egl.cc b/ui/gl/gl_fence_egl.cc
index ebc33bf..2ff18fc 100644
--- a/ui/gl/gl_fence_egl.cc
+++ b/ui/gl/gl_fence_egl.cc
@@ -42,6 +42,10 @@
}
void GLFenceEGL::ServerWait() {
+ if (!gfx::g_driver_egl.ext.b_EGL_KHR_wait_sync) {
+ ClientWait();
+ return;
+ }
if (!flush_event_.get() || flush_event_->IsSignaled()) {
EGLint flags = 0;
eglWaitSyncKHR(display_, sync_, flags);