replicant-frameworks_native/libs/binder/BufferedTextOutput.cpp
Christopher Tate ed7a50cc7d Prevent integer overflow when calculating buffer resizes
Make sure that we don't go haywire if an exponential buffer growth
operation winds up wrapping integer range.  Along the way, fix a
bookkeeping bug in BufferedTextOutput that would cause it to keep
spuriously realloc()ing on every append().

Bug 20674694

Change-Id: Ia845b7de36b90672a151a918ffc26c7da68e20a2
2015-06-08 14:49:09 -07:00

283 lines
7.6 KiB
C++

/*
* Copyright (C) 2006 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 <binder/BufferedTextOutput.h>
#include <binder/Debug.h>
#include <utils/Atomic.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
#include <cutils/threads.h>
#include <private/binder/Static.h>
#include <stdio.h>
#include <stdlib.h>
// ---------------------------------------------------------------------------
namespace android {
struct BufferedTextOutput::BufferState : public RefBase
{
BufferState(int32_t _seq)
: seq(_seq)
, buffer(NULL)
, bufferPos(0)
, bufferSize(0)
, atFront(true)
, indent(0)
, bundle(0) {
}
~BufferState() {
free(buffer);
}
status_t append(const char* txt, size_t len) {
if ((len+bufferPos) > bufferSize) {
size_t newSize = ((len+bufferPos)*3)/2;
if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow
void* b = realloc(buffer, newSize);
if (!b) return NO_MEMORY;
buffer = (char*)b;
bufferSize = newSize;
}
memcpy(buffer+bufferPos, txt, len);
bufferPos += len;
return NO_ERROR;
}
void restart() {
bufferPos = 0;
atFront = true;
if (bufferSize > 256) {
void* b = realloc(buffer, 256);
if (b) {
buffer = (char*)b;
bufferSize = 256;
}
}
}
const int32_t seq;
char* buffer;
size_t bufferPos;
size_t bufferSize;
bool atFront;
int32_t indent;
int32_t bundle;
};
struct BufferedTextOutput::ThreadState
{
Vector<sp<BufferedTextOutput::BufferState> > states;
};
static mutex_t gMutex;
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;
static int32_t allocBufferIndex()
{
int32_t res = -1;
mutex_lock(&gMutex);
if (gFreeBufferIndex >= 0) {
res = gFreeBufferIndex;
gFreeBufferIndex = gTextBuffers[res];
gTextBuffers.editItemAt(res) = -1;
} else {
res = gTextBuffers.size();
gTextBuffers.add(-1);
}
mutex_unlock(&gMutex);
return res;
}
static void freeBufferIndex(int32_t idx)
{
mutex_lock(&gMutex);
gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
gFreeBufferIndex = idx;
mutex_unlock(&gMutex);
}
// ---------------------------------------------------------------------------
BufferedTextOutput::BufferedTextOutput(uint32_t flags)
: mFlags(flags)
, mSeq(android_atomic_inc(&gSequence))
, mIndex(allocBufferIndex())
{
mGlobalState = new BufferState(mSeq);
if (mGlobalState) mGlobalState->incStrong(this);
}
BufferedTextOutput::~BufferedTextOutput()
{
if (mGlobalState) mGlobalState->decStrong(this);
freeBufferIndex(mIndex);
}
status_t BufferedTextOutput::print(const char* txt, size_t len)
{
//printf("BufferedTextOutput: printing %d\n", len);
AutoMutex _l(mLock);
BufferState* b = getBuffer();
const char* const end = txt+len;
status_t err;
while (txt < end) {
// Find the next line.
const char* first = txt;
while (txt < end && *txt != '\n') txt++;
// Include this and all following empty lines.
while (txt < end && *txt == '\n') txt++;
// Special cases for first data on a line.
if (b->atFront) {
if (b->indent > 0) {
// If this is the start of a line, add the indent.
const char* prefix = stringForIndent(b->indent);
err = b->append(prefix, strlen(prefix));
if (err != NO_ERROR) return err;
} else if (*(txt-1) == '\n' && !b->bundle) {
// Fast path: if we are not indenting or bundling, and
// have been given one or more complete lines, just write
// them out without going through the buffer.
// Slurp up all of the lines.
const char* lastLine = txt+1;
while (txt < end) {
if (*txt++ == '\n') lastLine = txt;
}
struct iovec vec;
vec.iov_base = (void*)first;
vec.iov_len = lastLine-first;
//printf("Writing %d bytes of data!\n", vec.iov_len);
writeLines(vec, 1);
txt = lastLine;
continue;
}
}
// Append the new text to the buffer.
err = b->append(first, txt-first);
if (err != NO_ERROR) return err;
b->atFront = *(txt-1) == '\n';
// If we have finished a line and are not bundling, write
// it out.
//printf("Buffer is now %d bytes\n", b->bufferPos);
if (b->atFront && !b->bundle) {
struct iovec vec;
vec.iov_base = b->buffer;
vec.iov_len = b->bufferPos;
//printf("Writing %d bytes of data!\n", vec.iov_len);
writeLines(vec, 1);
b->restart();
}
}
return NO_ERROR;
}
void BufferedTextOutput::moveIndent(int delta)
{
AutoMutex _l(mLock);
BufferState* b = getBuffer();
b->indent += delta;
if (b->indent < 0) b->indent = 0;
}
void BufferedTextOutput::pushBundle()
{
AutoMutex _l(mLock);
BufferState* b = getBuffer();
b->bundle++;
}
void BufferedTextOutput::popBundle()
{
AutoMutex _l(mLock);
BufferState* b = getBuffer();
b->bundle--;
LOG_FATAL_IF(b->bundle < 0,
"TextOutput::popBundle() called more times than pushBundle()");
if (b->bundle < 0) b->bundle = 0;
if (b->bundle == 0) {
// Last bundle, write out data if it is complete. If it is not
// complete, don't write until the last line is done... this may
// or may not be the write thing to do, but it's the easiest.
if (b->bufferPos > 0 && b->atFront) {
struct iovec vec;
vec.iov_base = b->buffer;
vec.iov_len = b->bufferPos;
writeLines(vec, 1);
b->restart();
}
}
}
BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
{
if ((mFlags&MULTITHREADED) != 0) {
ThreadState* ts = getThreadState();
if (ts) {
while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL);
BufferState* bs = ts->states[mIndex].get();
if (bs != NULL && bs->seq == mSeq) return bs;
ts->states.editItemAt(mIndex) = new BufferState(mIndex);
bs = ts->states[mIndex].get();
if (bs != NULL) return bs;
}
}
return mGlobalState;
}
}; // namespace android