verify embedded buffer matches address in parent am: fcd86bd0d1 am: 68b6a9027e am: 2e1f5e023c am: 41202fc9c6 am: f39fa2cd1c am: b85ad8670a
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/libhwbinder/+/14314385
Change-Id: Ica7b015ab06332f30b3118f9371b6be6e286f100
diff --git a/Android.bp b/Android.bp
index ae84ebb..6f2869c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,10 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["system_libhwbinder_license"],
+}
+
+// Added automatically by a large-scale-change
+// http://go/android-license-faq
+license {
+ name: "system_libhwbinder_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
+cc_library_headers {
+ name: "libhwbinder_headers",
+ export_include_dirs: ["include"],
+ host_supported: true,
+ recovery_available: true,
+ vendor_available: true,
+ product_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "29",
+}
+
cc_defaults {
name: "libhwbinder_defaults",
- export_include_dirs: ["include"],
+ header_libs: ["libhwbinder_headers"],
+ export_header_lib_headers: ["libhwbinder_headers"],
sanitize: {
misc_undefined: ["integer"],
@@ -31,6 +65,7 @@
"ProcessState.cpp",
"Static.cpp",
"TextOutput.cpp",
+ "Utils.cpp",
],
product_variables: {
@@ -60,13 +95,17 @@
}
// WARNING: this should no longer be used
+// This is automatically removed by bpfix. Once there are no makefiles, fixes can be automatically applied, and this can be removed.
cc_library {
name: "libhwbinder",
vendor_available: true,
export_include_dirs: ["include"],
- visibility: [":__subpackages__"],
+ visibility: [
+ ":__subpackages__",
+ "//vendor:__subpackages__",
+ ],
}
// Combined into libhidlbase for efficiency.
@@ -88,6 +127,7 @@
host_supported: true,
recovery_available: true,
vendor_available: true,
+ product_available: true,
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
apex_available: [
diff --git a/Binder.cpp b/Binder.cpp
index 9edd27b..b90639f 100644
--- a/Binder.cpp
+++ b/Binder.cpp
@@ -16,15 +16,21 @@
#include <hwbinder/Binder.h>
-#include <atomic>
-#include <utils/misc.h>
+#include <android-base/macros.h>
+#include <cutils/android_filesystem_config.h>
+#include <cutils/multiuser.h>
#include <hwbinder/BpHwBinder.h>
#include <hwbinder/IInterface.h>
+#include <hwbinder/IPCThreadState.h>
#include <hwbinder/Parcel.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
#include <linux/sched.h>
#include <stdio.h>
+#include <atomic>
+
namespace android {
namespace hardware {
@@ -110,6 +116,19 @@
{
data.setDataPosition(0);
+ if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {
+ reply->markSensitive();
+ }
+
+ // extra comment to try to force running all tests
+ if (UNLIKELY(code == HIDL_DEBUG_TRANSACTION)) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (multiuser_get_app_id(uid) >= AID_APP_START) {
+ ALOGE("Can not call IBase::debug from apps");
+ return PERMISSION_DENIED;
+ }
+ }
+
status_t err = NO_ERROR;
switch (code) {
default:
diff --git a/BufferedTextOutput.cpp b/BufferedTextOutput.cpp
index 5addba4..a0152b1 100644
--- a/BufferedTextOutput.cpp
+++ b/BufferedTextOutput.cpp
@@ -16,15 +16,14 @@
#define LOG_TAG "hw-BufferedTextOutput"
-#include <hwbinder/BufferedTextOutput.h>
#include <hwbinder/Debug.h>
#include <cutils/atomic.h>
-#include <cutils/threads.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
+#include "BufferedTextOutput.h"
#include <hwbinder/Static.h>
#include <pthread.h>
@@ -94,22 +93,6 @@
static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
-static thread_store_t tls;
-
-BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
-{
- ThreadState* ts = (ThreadState*) thread_store_get( &tls );
- if (ts) return ts;
- ts = new ThreadState;
- thread_store_set( &tls, ts, threadDestructor );
- return ts;
-}
-
-void BufferedTextOutput::threadDestructor(void *st)
-{
- delete ((ThreadState*)st);
-}
-
static volatile int32_t gSequence = 0;
static volatile int32_t gFreeBufferIndex = -1;
@@ -264,16 +247,14 @@
BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
{
if ((mFlags&MULTITHREADED) != 0) {
- ThreadState* ts = getThreadState();
- if (ts) {
- while (ts->states.size() <= (size_t)mIndex) ts->states.add(nullptr);
- BufferState* bs = ts->states[mIndex].get();
- if (bs != nullptr && bs->seq == mSeq) return bs;
+ thread_local ThreadState ts;
+ while (ts.states.size() <= (size_t)mIndex) ts.states.add(nullptr);
+ BufferState* bs = ts.states[mIndex].get();
+ if (bs != nullptr && bs->seq == mSeq) return bs;
- ts->states.editItemAt(mIndex) = new BufferState(mIndex);
- bs = ts->states[mIndex].get();
- if (bs != nullptr) return bs;
- }
+ ts.states.editItemAt(mIndex) = new BufferState(mIndex);
+ bs = ts.states[mIndex].get();
+ if (bs != nullptr) return bs;
}
return mGlobalState;
diff --git a/include/hwbinder/BufferedTextOutput.h b/BufferedTextOutput.h
similarity index 93%
rename from include/hwbinder/BufferedTextOutput.h
rename to BufferedTextOutput.h
index 3541659..c8042aa 100644
--- a/include/hwbinder/BufferedTextOutput.h
+++ b/BufferedTextOutput.h
@@ -17,7 +17,8 @@
#ifndef ANDROID_HARDWARE_BUFFEREDTEXTOUTPUT_H
#define ANDROID_HARDWARE_BUFFEREDTEXTOUTPUT_H
-#include <hwbinder/TextOutput.h>
+#include "TextOutput.h"
+
#include <utils/threads.h>
#include <sys/uio.h>
@@ -49,9 +50,6 @@
struct BufferState;
struct ThreadState;
- static ThreadState*getThreadState();
- static void threadDestructor(void *st);
-
BufferState*getBuffer() const;
uint32_t mFlags;
diff --git a/IPCThreadState.cpp b/IPCThreadState.cpp
index 2f4464d..aa050fc 100644
--- a/IPCThreadState.cpp
+++ b/IPCThreadState.cpp
@@ -20,7 +20,6 @@
#include <hwbinder/Binder.h>
#include <hwbinder/BpHwBinder.h>
-#include <hwbinder/TextOutput.h>
#include <android-base/macros.h>
#include <utils/CallStack.h>
@@ -30,6 +29,7 @@
#include "binder_kernel.h"
#include <hwbinder/Static.h>
+#include "TextOutput.h"
#include <atomic>
#include <errno.h>
@@ -88,6 +88,8 @@
"BR_DEAD_BINDER",
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
"BR_FAILED_REPLY",
+ "BR_FROZEN_REPLY",
+ "BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_SEC_CTX",
};
@@ -407,7 +409,7 @@
void IPCThreadState::flushCommands()
{
- if (mProcess->mDriverFD <= 0)
+ if (mProcess->mDriverFD < 0)
return;
talkWithDriver(false);
// The flush could have caused post-write refcount decrements to have
@@ -421,18 +423,6 @@
}
}
-void IPCThreadState::blockUntilThreadAvailable()
-{
- pthread_mutex_lock(&mProcess->mThreadCountLock);
- while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) {
- ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n",
- static_cast<unsigned long>(mProcess->mExecutingThreadsCount),
- static_cast<unsigned long>(mProcess->mMaxThreads));
- pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock);
- }
- pthread_mutex_unlock(&mProcess->mThreadCountLock);
-}
-
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
@@ -472,7 +462,6 @@
}
mProcess->mStarvationStartTimeMs = 0;
}
- pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
@@ -580,7 +569,7 @@
int IPCThreadState::setupPolling(int* fd)
{
- if (mProcess->mDriverFD <= 0) {
+ if (mProcess->mDriverFD < 0) {
return -EBADF;
}
@@ -815,6 +804,11 @@
}
switch (cmd) {
+ case BR_ONEWAY_SPAM_SUSPECT:
+ ALOGE("Process seems to be sending too many oneway calls.");
+ CallStack::logStack("oneway spamming", CallStack::getCurrent().get(),
+ ANDROID_LOG_ERROR);
+ [[fallthrough]];
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
@@ -889,7 +883,7 @@
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
- if (mProcess->mDriverFD <= 0) {
+ if (mProcess->mDriverFD < 0) {
return -EBADF;
}
@@ -946,7 +940,7 @@
#else
err = INVALID_OPERATION;
#endif
- if (mProcess->mDriverFD <= 0) {
+ if (mProcess->mDriverFD < 0) {
err = -EBADF;
}
IF_LOG_COMMANDS() {
@@ -1183,6 +1177,8 @@
<< reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
}
+ constexpr size_t kForwardReplyFlags = TF_CLEAR_BUF;
+
auto reply_callback = [&] (auto &replyParcel) {
if (reply_sent) {
// Reply was sent earlier, ignore it.
@@ -1192,7 +1188,7 @@
reply_sent = true;
if ((tr.flags & TF_ONE_WAY) == 0) {
replyParcel.setError(NO_ERROR);
- sendReply(replyParcel, 0);
+ sendReply(replyParcel, (tr.flags & kForwardReplyFlags));
} else {
ALOGE("Not sending reply in one-way transaction");
}
@@ -1219,7 +1215,7 @@
// Should have been a reply but there wasn't, so there
// must have been an error instead.
reply.setError(error);
- sendReply(reply, 0);
+ sendReply(reply, (tr.flags & kForwardReplyFlags));
} else {
if (error != NO_ERROR) {
ALOGE("transact() returned error after sending reply.");
@@ -1297,7 +1293,7 @@
if (self) {
self->flushCommands();
#if defined(__ANDROID__)
- if (self->mProcess->mDriverFD > 0) {
+ if (self->mProcess->mDriverFD >= 0) {
ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
}
#endif
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/Parcel.cpp b/Parcel.cpp
index 2c6bf5d..b5648a5 100644
--- a/Parcel.cpp
+++ b/Parcel.cpp
@@ -35,10 +35,8 @@
#include <hwbinder/IPCThreadState.h>
#include <hwbinder/Parcel.h>
#include <hwbinder/ProcessState.h>
-#include <hwbinder/TextOutput.h>
#include <cutils/ashmem.h>
-#include <utils/Debug.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include <utils/String8.h>
@@ -46,6 +44,10 @@
#include "binder_kernel.h"
#include <hwbinder/Static.h>
+#include "TextOutput.h"
+#include "Utils.h"
+
+#include <atomic>
#define LOG_REFS(...)
//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -74,9 +76,8 @@
namespace android {
namespace hardware {
-static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
-static size_t gParcelGlobalAllocSize = 0;
-static size_t gParcelGlobalAllocCount = 0;
+static std::atomic<size_t> gParcelGlobalAllocCount;
+static std::atomic<size_t> gParcelGlobalAllocSize;
static size_t gMaxFds = 0;
@@ -261,17 +262,11 @@
}
size_t Parcel::getGlobalAllocSize() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t size = gParcelGlobalAllocSize;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return size;
+ return gParcelGlobalAllocSize.load();
}
size_t Parcel::getGlobalAllocCount() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t count = gParcelGlobalAllocCount;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return count;
+ return gParcelGlobalAllocCount.load();
}
const uint8_t* Parcel::data() const
@@ -361,6 +356,11 @@
return err;
}
+void Parcel::markSensitive() const
+{
+ mDeallocZero = true;
+}
+
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const char* interface)
{
@@ -470,7 +470,7 @@
const size_t padded = pad_size(len);
- // sanity check for integer overflow
+ // validate for integer overflow
if (mDataPos+padded < mDataPos) {
return nullptr;
}
@@ -889,11 +889,6 @@
parent_buffer_handle, parent_offset);
}
-void Parcel::remove(size_t /*start*/, size_t /*amt*/)
-{
- LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
-}
-
status_t Parcel::read(void* outData, size_t len) const
{
if (len > INT32_MAX) {
@@ -932,7 +927,7 @@
template<class T>
status_t Parcel::readAligned(T *pArg) const {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(T)) <= mDataSize) {
const void* data = mData+mDataPos;
@@ -956,7 +951,7 @@
template<class T>
status_t Parcel::writeAligned(T val) {
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
+ static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
@@ -1731,16 +1726,11 @@
releaseObjects();
if (mData) {
LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- if (mDataCapacity <= gParcelGlobalAllocSize) {
- gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity;
- } else {
- gParcelGlobalAllocSize = 0;
+ gParcelGlobalAllocSize -= mDataCapacity;
+ gParcelGlobalAllocCount--;
+ if (mDeallocZero) {
+ zeroMemory(mData, mDataSize);
}
- if (gParcelGlobalAllocCount > 0) {
- gParcelGlobalAllocCount--;
- }
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
free(mData);
}
if (mObjects) free(mObjects);
@@ -1760,6 +1750,21 @@
return continueWrite(newSize);
}
+static uint8_t* reallocZeroFree(uint8_t* data, size_t oldCapacity, size_t newCapacity, bool zero) {
+ if (!zero) {
+ return (uint8_t*)realloc(data, newCapacity);
+ }
+ uint8_t* newData = (uint8_t*)malloc(newCapacity);
+ if (!newData) {
+ return nullptr;
+ }
+
+ memcpy(newData, data, std::min(oldCapacity, newCapacity));
+ zeroMemory(data, oldCapacity);
+ free(data);
+ return newData;
+}
+
status_t Parcel::restartWrite(size_t desired)
{
if (desired > INT32_MAX) {
@@ -1773,7 +1778,7 @@
return continueWrite(desired);
}
- uint8_t* data = (uint8_t*)realloc(mData, desired);
+ uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (!data && desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
@@ -1781,15 +1786,17 @@
releaseObjects();
- if (data) {
+ if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- gParcelGlobalAllocSize += desired;
- gParcelGlobalAllocSize -= mDataCapacity;
+ if (mDataCapacity > desired) {
+ gParcelGlobalAllocSize -= (mDataCapacity - desired);
+ } else {
+ gParcelGlobalAllocSize += (desired - mDataCapacity);
+ }
+
if (!mData) {
gParcelGlobalAllocCount++;
}
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
}
@@ -1877,10 +1884,8 @@
mOwner = nullptr;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mObjects = objects;
@@ -1923,14 +1928,12 @@
// We own the data, so we can just do a realloc().
if (desired > mDataCapacity) {
- uint8_t* data = (uint8_t*)realloc(mData, desired);
+ uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocSize -= mDataCapacity;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
} else {
@@ -1962,10 +1965,8 @@
}
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataSize = mDataPos = 0;
@@ -1994,6 +1995,7 @@
mHasFds = false;
mFdsKnown = true;
mAllowFds = true;
+ mDeallocZero = false;
mOwner = nullptr;
clearCache();
diff --git a/ProcessState.cpp b/ProcessState.cpp
index 694efd1..d329657 100644
--- a/ProcessState.cpp
+++ b/ProcessState.cpp
@@ -40,6 +40,7 @@
#define DEFAULT_BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 0
+#define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1
// -------------------------------------------------------------------------
@@ -66,83 +67,40 @@
sp<ProcessState> ProcessState::self()
{
- Mutex::Autolock _l(gProcessMutex);
- if (gProcess != nullptr) {
- return gProcess;
- }
- gProcess = new ProcessState(DEFAULT_BINDER_VM_SIZE);
- return gProcess;
+ return init(DEFAULT_BINDER_VM_SIZE, false /*requireMmapSize*/);
}
sp<ProcessState> ProcessState::selfOrNull() {
- Mutex::Autolock _l(gProcessMutex);
- return gProcess;
+ return init(0, false /*requireMmapSize*/);
}
-sp<ProcessState> ProcessState::initWithMmapSize(size_t mmap_size) {
- Mutex::Autolock _l(gProcessMutex);
- if (gProcess != nullptr) {
- LOG_ALWAYS_FATAL_IF(mmap_size != gProcess->getMmapSize(),
- "ProcessState already initialized with a different mmap size.");
+sp<ProcessState> ProcessState::initWithMmapSize(size_t mmapSize) {
+ return init(mmapSize, true /*requireMmapSize*/);
+}
+
+sp<ProcessState> ProcessState::init(size_t mmapSize, bool requireMmapSize) {
+ [[clang::no_destroy]] static sp<ProcessState> gProcess;
+ [[clang::no_destroy]] static std::mutex gProcessMutex;
+
+ if (mmapSize == 0) {
+ std::lock_guard<std::mutex> l(gProcessMutex);
return gProcess;
}
- gProcess = new ProcessState(mmap_size);
+ [[clang::no_destroy]] static std::once_flag gProcessOnce;
+ std::call_once(gProcessOnce, [&](){
+ std::lock_guard<std::mutex> l(gProcessMutex);
+ gProcess = new ProcessState(mmapSize);
+ });
+
+ if (requireMmapSize) {
+ LOG_ALWAYS_FATAL_IF(mmapSize != gProcess->getMmapSize(),
+ "ProcessState already initialized with a different mmap size.");
+ }
+
return gProcess;
}
-void ProcessState::setContextObject(const sp<IBinder>& object)
-{
- setContextObject(object, String16("default"));
-}
-
-sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
-{
- return getStrongProxyForHandle(0);
-}
-
-void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name)
-{
- AutoMutex _l(mLock);
- mContexts.add(name, object);
-}
-
-sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinder>& caller)
-{
- mLock.lock();
- sp<IBinder> object(
- mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : nullptr);
- mLock.unlock();
-
- //printf("Getting context object %s for %p\n", String8(name).string(), caller.get());
-
- if (object != nullptr) return object;
-
- // Don't attempt to retrieve contexts if we manage them
- if (mManagesContexts) {
- ALOGE("getContextObject(%s) failed, but we manage the contexts!\n",
- String8(name).string());
- return nullptr;
- }
-
- IPCThreadState* ipc = IPCThreadState::self();
- {
- Parcel data, reply;
- // no interface token on this magic transaction
- data.writeString16(name);
- data.writeStrongBinder(caller);
- status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0);
- if (result == NO_ERROR) {
- object = reply.readStrongBinder();
- }
- }
-
- ipc->flushCommands();
-
- if (object != nullptr) setContextObject(object, name);
- return object;
-}
-
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
@@ -154,41 +112,32 @@
}
}
-bool ProcessState::isContextManager(void) const
+sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
- return mManagesContexts;
+ return getStrongProxyForHandle(0);
}
-bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+void ProcessState::becomeContextManager()
{
- if (!mManagesContexts) {
- AutoMutex _l(mLock);
- mBinderContextCheckFunc = checkFunc;
- mBinderContextUserData = userData;
+ AutoMutex _l(mLock);
- flat_binder_object obj {
- .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
- };
+ flat_binder_object obj {
+ .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
+ };
- status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+ status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
- // fallback to original method
- if (result != 0) {
- android_errorWriteLog(0x534e4554, "121035042");
+ // fallback to original method
+ if (result != 0) {
+ android_errorWriteLog(0x534e4554, "121035042");
- int dummy = 0;
- result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
- }
-
- if (result == 0) {
- mManagesContexts = true;
- } else if (result == -1) {
- mBinderContextCheckFunc = nullptr;
- mBinderContextUserData = nullptr;
- ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
- }
+ int unused = 0;
+ result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused);
}
- return mManagesContexts;
+
+ if (result == -1) {
+ ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
+ }
}
// Get references to userspace objects held by the kernel binder driver
@@ -359,6 +308,9 @@
}
status_t ProcessState::setThreadPoolConfiguration(size_t maxThreads, bool callerJoinsPool) {
+ LOG_ALWAYS_FATAL_IF(mThreadPoolStarted && maxThreads < mMaxThreads,
+ "Binder threadpool cannot be shrunk after starting");
+
// if the caller joins the pool, then there will be one thread which is impossible.
LOG_ALWAYS_FATAL_IF(maxThreads == 0 && callerJoinsPool,
"Binder threadpool must have a minimum of one thread if caller joins pool.");
@@ -392,6 +344,15 @@
return NO_ERROR;
}
+status_t ProcessState::enableOnewaySpamDetection(bool enable) {
+ uint32_t enableDetection = enable ? 1 : 0;
+ if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
+ ALOGI("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+ return -errno;
+ }
+ return NO_ERROR;
+}
+
size_t ProcessState::getMaxThreads() {
return mMaxThreads;
}
@@ -421,27 +382,28 @@
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
+ uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
+ result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
+ if (result == -1) {
+ ALOGI("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+ }
} else {
ALOGW("Opening '/dev/hwbinder' failed: %s\n", strerror(errno));
}
return fd;
}
-ProcessState::ProcessState(size_t mmap_size)
+ProcessState::ProcessState(size_t mmapSize)
: mDriverFD(open_driver())
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
- , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
- , mManagesContexts(false)
- , mBinderContextCheckFunc(nullptr)
- , mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mSpawnThreadOnStart(true)
, mThreadPoolSeq(1)
- , mMmapSize(mmap_size)
+ , mMmapSize(mmapSize)
, mCallRestriction(CallRestriction::NONE)
{
if (mDriverFD >= 0) {
diff --git a/Static.cpp b/Static.cpp
index 2fb12e6..305f5f2 100644
--- a/Static.cpp
+++ b/Static.cpp
@@ -19,7 +19,8 @@
#include <hwbinder/Static.h>
-#include <hwbinder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
+
#include <hwbinder/IPCThreadState.h>
#include <utils/Log.h>
@@ -49,10 +50,5 @@
static LogTextOutput gLogTextOutput;
TextOutput& alog(gLogTextOutput);
-// ------------ ProcessState.cpp
-
-Mutex& gProcessMutex = *new Mutex;
-sp<ProcessState> gProcess;
-
} // namespace hardware
} // namespace android
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2bd0463..dfede5a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,6 +2,14 @@
"presubmit": [
{
"name": "libbinderthreadstateutils_test"
+ },
+ {
+ "name": "SettingsGoogleUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/TextOutput.cpp b/TextOutput.cpp
index e6e8cb7..5628f5c 100644
--- a/TextOutput.cpp
+++ b/TextOutput.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <hwbinder/TextOutput.h>
+#include "TextOutput.h"
#include <hwbinder/Debug.h>
diff --git a/include/hwbinder/TextOutput.h b/TextOutput.h
similarity index 100%
rename from include/hwbinder/TextOutput.h
rename to TextOutput.h
diff --git a/Utils.cpp b/Utils.cpp
new file mode 100644
index 0000000..5a29d6b
--- /dev/null
+++ b/Utils.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "Utils.h"
+
+#include <string.h>
+
+namespace android::hardware {
+
+void zeroMemory(uint8_t* data, size_t size) {
+ memset(data, 0, size);
+}
+
+} // namespace android::hardware
diff --git a/Utils.h b/Utils.h
new file mode 100644
index 0000000..07a5e69
--- /dev/null
+++ b/Utils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <cstdint>
+#include <stddef.h>
+
+namespace android::hardware {
+
+// avoid optimizations
+void zeroMemory(uint8_t* data, size_t size);
+
+} // namespace android::hardware
diff --git a/binder_kernel.h b/binder_kernel.h
index fdf5b1e..2695f51 100644
--- a/binder_kernel.h
+++ b/binder_kernel.h
@@ -17,15 +17,26 @@
#ifndef ANDROID_HARDWARE_BINDER_KERNEL_H
#define ANDROID_HARDWARE_BINDER_KERNEL_H
-/**
- * Only need this file to fix the __packed__ keyword.
- */
-
// TODO(b/31559095): bionic on host
#ifndef __ANDROID__
#define __packed __attribute__((__packed__))
#endif
+#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#ifndef BR_ONEWAY_SPAM_SUSPECT
+// Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production
+// this will come from UAPI binder.h
+#define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19)
+#endif //BR_ONEWAY_SPAM_SUSPECT
+
+#ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+/*
+ * Temporary definitions for oneway spam detection support. For the final version
+ * these will be defined in the UAPI binder.h file from upstream kernel.
+ */
+#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
+#endif //BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+
#endif // ANDROID_HARDWARE_BINDER_KERNEL_H
diff --git a/include/hwbinder/Binder.h b/include/hwbinder/Binder.h
index 88c155f..d05c6ff 100644
--- a/include/hwbinder/Binder.h
+++ b/include/hwbinder/Binder.h
@@ -21,6 +21,10 @@
#include <stdint.h>
#include <hwbinder/IBinder.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
// ---------------------------------------------------------------------------
namespace android {
namespace hardware {
@@ -69,6 +73,9 @@
TransactCallback callback = nullptr);
// This must be called before the object is sent to another process. Not thread safe.
+ //
+ // If this is called with true, and the kernel supports it,
+ // IPCThreadState::getCallingSid will return values for remote processes.
void setRequestingSid(bool requestSid);
int mSchedPolicy; // policy to run transaction from this node at
diff --git a/include/hwbinder/BpHwBinder.h b/include/hwbinder/BpHwBinder.h
index a5b2245..36f82e1 100644
--- a/include/hwbinder/BpHwBinder.h
+++ b/include/hwbinder/BpHwBinder.h
@@ -21,6 +21,10 @@
#include <utils/KeyedVector.h>
#include <utils/threads.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
// ---------------------------------------------------------------------------
namespace android {
namespace hardware {
@@ -114,7 +118,6 @@
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
ObjectManager mObjects;
- Parcel* mConstantData;
mutable String16 mDescriptorCache;
};
diff --git a/include/hwbinder/Debug.h b/include/hwbinder/Debug.h
index c23618c..cdc5fa0 100644
--- a/include/hwbinder/Debug.h
+++ b/include/hwbinder/Debug.h
@@ -21,6 +21,10 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
namespace android {
namespace hardware {
// ---------------------------------------------------------------------------
diff --git a/include/hwbinder/IBinder.h b/include/hwbinder/IBinder.h
index bc1f733..b5593fe 100644
--- a/include/hwbinder/IBinder.h
+++ b/include/hwbinder/IBinder.h
@@ -23,6 +23,10 @@
#include <utils/RefBase.h>
#include <utils/String16.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
// ---------------------------------------------------------------------------
namespace android {
namespace hardware {
@@ -45,8 +49,40 @@
using TransactCallback = std::function<void(Parcel&)>;
enum {
+ /* It is very important that these values NEVER change. These values
+ * must remain unchanged over the lifetime of android. This is
+ * because the framework on a device will be updated independently of
+ * the hals on a device. If the hals are compiled with one set of
+ * transaction values, and the framework with another, then the
+ * interface between them will be destroyed, and the device will not
+ * work.
+ */
+ /////////////////// User defined transactions
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x0effffff,
+ /////////////////// HIDL reserved
+#define B_PACK_CHARS_USER(c1, c2, c3, c4) \
+ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
+ FIRST_HIDL_TRANSACTION = 0x0f000000,
+ HIDL_PING_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'P', 'N', 'G'),
+ HIDL_DESCRIPTOR_CHAIN_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'C', 'H', 'N'),
+ HIDL_GET_DESCRIPTOR_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'D', 'S', 'C'),
+ HIDL_SYSPROPS_CHANGED_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'S', 'Y', 'S'),
+ HIDL_LINK_TO_DEATH_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'L', 'T', 'D'),
+ HIDL_UNLINK_TO_DEATH_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'U', 'T', 'D'),
+ HIDL_SET_HAL_INSTRUMENTATION_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'I', 'N', 'T'),
+ HIDL_GET_REF_INFO_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'R', 'E', 'F'),
+ HIDL_DEBUG_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'D', 'B', 'G'),
+ HIDL_HASH_CHAIN_TRANSACTION = B_PACK_CHARS_USER(0x0f, 'H', 'S', 'H'),
+#undef B_PACK_CHARS_USER
+ LAST_HIDL_TRANSACTION = 0x0fffffff,
+
// Corresponds to TF_ONE_WAY -- an asynchronous call.
- FLAG_ONEWAY = 0x00000001
+ FLAG_ONEWAY = 0x00000001,
+
+ // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
+ // is made
+ FLAG_CLEAR_BUF = 0x00000020,
};
IBinder();
diff --git a/include/hwbinder/IInterface.h b/include/hwbinder/IInterface.h
index 7e13957..7fec75f 100644
--- a/include/hwbinder/IInterface.h
+++ b/include/hwbinder/IInterface.h
@@ -20,6 +20,10 @@
#include <hwbinder/Binder.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
namespace android {
namespace hardware {
// ----------------------------------------------------------------------
diff --git a/include/hwbinder/IPCThreadState.h b/include/hwbinder/IPCThreadState.h
index ca99591..f4c9f9d 100644
--- a/include/hwbinder/IPCThreadState.h
+++ b/include/hwbinder/IPCThreadState.h
@@ -24,6 +24,10 @@
#include <functional>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
#if defined(_WIN32)
typedef int uid_t;
#endif
@@ -87,10 +91,6 @@
static void shutdown();
- // Call blocks until the number of executing binder threads is less than
- // the maximum number of binder threads threads allowed for this process.
- void blockUntilThreadAvailable();
-
// Service manager registration
void setTheContextObject(sp<BHwBinder> obj);
diff --git a/include/hwbinder/Parcel.h b/include/hwbinder/Parcel.h
index d3bdfe6..4822fa0 100644
--- a/include/hwbinder/Parcel.h
+++ b/include/hwbinder/Parcel.h
@@ -27,6 +27,10 @@
#include <hwbinder/IInterface.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
struct binder_buffer_object;
struct flat_binder_object;
@@ -66,6 +70,13 @@
status_t setData(const uint8_t* buffer, size_t len);
+ // Zeros data when reallocating. Other mitigations may be added
+ // in the future.
+ //
+ // WARNING: some read methods may make additional copies of data.
+ // In order to verify this, heap dumps should be used.
+ void markSensitive() const;
+
// Writes the RPC header.
status_t writeInterfaceToken(const char* interface);
@@ -118,8 +129,6 @@
size_t parent_offset = 0);
status_t writeNativeHandleNoDup(const native_handle* handle);
- void remove(size_t start, size_t amt);
-
status_t read(void* outData, size_t len) const;
const void* readInplace(size_t len) const;
status_t readInt8(int8_t *pArg) const;
@@ -294,6 +303,10 @@
mutable bool mHasFds;
bool mAllowFds;
+ // if this parcelable is involved in a secure transaction, force the
+ // data to be overridden with zero when deallocated
+ mutable bool mDeallocZero;
+
release_func mOwner;
void* mOwnerCookie;
};
diff --git a/include/hwbinder/ProcessState.h b/include/hwbinder/ProcessState.h
index 91337d8..56ac846 100644
--- a/include/hwbinder/ProcessState.h
+++ b/include/hwbinder/ProcessState.h
@@ -26,6 +26,10 @@
#include <pthread.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
// ---------------------------------------------------------------------------
namespace android {
namespace hardware {
@@ -38,26 +42,14 @@
static sp<ProcessState> self();
static sp<ProcessState> selfOrNull();
// Note: don't call self() or selfOrNull() before initWithMmapSize()
+ // with '0' as an argument, this is the same as selfOrNull
static sp<ProcessState> initWithMmapSize(size_t mmapSize); // size in bytes
- void setContextObject(const sp<IBinder>& object);
- sp<IBinder> getContextObject(const sp<IBinder>& caller);
-
- void setContextObject(const sp<IBinder>& object,
- const String16& name);
- sp<IBinder> getContextObject(const String16& name,
- const sp<IBinder>& caller);
-
void startThreadPool();
- typedef bool (*context_check_func)(const String16& name,
- const sp<IBinder>& caller,
- void* userData);
-
- bool isContextManager(void) const;
- bool becomeContextManager(
- context_check_func checkFunc,
- void* userData);
+ sp<IBinder> getContextObject(const sp<IBinder>& /*caller*/);
+ // only call once, without creating a pool
+ void becomeContextManager();
sp<IBinder> getStrongProxyForHandle(int32_t handle);
wp<IBinder> getWeakProxyForHandle(int32_t handle);
@@ -66,6 +58,7 @@
void spawnPooledThread(bool isMain);
status_t setThreadPoolConfiguration(size_t maxThreads, bool callerJoinsPool);
+ status_t enableOnewaySpamDetection(bool enable);
size_t getMaxThreads();
void giveThreadPoolName();
@@ -91,8 +84,10 @@
void setCallRestriction(CallRestriction restriction);
private:
+ static sp<ProcessState> init(size_t mmapSize, bool requireMmapSize);
+
friend class IPCThreadState;
- explicit ProcessState(size_t mmap_size);
+ explicit ProcessState(size_t mmapSize);
~ProcessState();
ProcessState(const ProcessState& o);
@@ -111,7 +106,6 @@
// Protects thread count variable below.
pthread_mutex_t mThreadCountLock;
- pthread_cond_t mThreadCountDecrement;
// Number of binder threads current executing a command.
size_t mExecutingThreadsCount;
// Maximum number for binder threads allowed for this process.
@@ -123,14 +117,6 @@
Vector<handle_entry>mHandleToObject;
- bool mManagesContexts;
- context_check_func mBinderContextCheckFunc;
- void* mBinderContextUserData;
-
- KeyedVector<String16, sp<IBinder> >
- mContexts;
-
-
String8 mRootDir;
bool mThreadPoolStarted;
bool mSpawnThreadOnStart;
diff --git a/include/hwbinder/Static.h b/include/hwbinder/Static.h
index 4b84c89..75bbcea 100644
--- a/include/hwbinder/Static.h
+++ b/include/hwbinder/Static.h
@@ -22,15 +22,15 @@
#include <hwbinder/IBinder.h>
#include <hwbinder/ProcessState.h>
+// WARNING: this code is part of libhwbinder, a fork of libbinder. Generally,
+// this means that it is only relevant to HIDL. Any AIDL- or libbinder-specific
+// code should not try to use these things.
+
namespace android {
namespace hardware {
// For TextStream.cpp
extern Vector<int32_t> gTextBuffers;
-// For ProcessState.cpp
-extern Mutex& gProcessMutex;
-extern sp<ProcessState> gProcess;
-
} // namespace hardware
} // namespace android
diff --git a/vts/performance/Android.bp b/vts/performance/Android.bp
index 35f61d5..9508c83 100644
--- a/vts/performance/Android.bp
+++ b/vts/performance/Android.bp
@@ -14,6 +14,14 @@
// limitations under the License.
//
+package {
+ // http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // the below license kinds from "system_libhwbinder_license":
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_libhwbinder_license"],
+}
+
cc_defaults {
name: "libhwbinder_test_defaults",
defaults: ["hwbinder_benchmark_pgo",],
diff --git a/vts/performance/Benchmark.cpp b/vts/performance/Benchmark.cpp
index e7d75cd..f995ec7 100644
--- a/vts/performance/Benchmark.cpp
+++ b/vts/performance/Benchmark.cpp
@@ -106,7 +106,7 @@
}
int main(int argc, char* argv []) {
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::details::setTrebleTestingOverride(true);
enum HwBinderMode {
kBinderize = 0,
diff --git a/vts/performance/Benchmark_throughput.cpp b/vts/performance/Benchmark_throughput.cpp
index 42c1c6f..d197d2d 100644
--- a/vts/performance/Benchmark_throughput.cpp
+++ b/vts/performance/Benchmark_throughput.cpp
@@ -28,6 +28,7 @@
#include <android/hardware/tests/libhwbinder/1.0/IBenchmark.h>
#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
using namespace std;
using namespace android;
@@ -297,7 +298,7 @@
}
int main(int argc, char *argv[]) {
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::details::setTrebleTestingOverride(true);
enum HwBinderMode {
kBinderize = 0,
diff --git a/vts/performance/Latency.cpp b/vts/performance/Latency.cpp
index a4c0751..13a93ad 100644
--- a/vts/performance/Latency.cpp
+++ b/vts/performance/Latency.cpp
@@ -282,7 +282,7 @@
// atrace --async_start -c sched idle workq binder_driver freq && \
// libhwbinder_latency -i 10000 -pair 4 -trace
int main(int argc, char** argv) {
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::details::setTrebleTestingOverride(true);
vector<Pipe> client_pipes;
vector<Pipe> service_pipes;