Arquivos
hhvm/hphp/runtime/base/util/string_buffer.cpp
T
ptarjan 0038b76a58 kill TAINTED code
While I was working on the TestCodeRun refactor I found two tests about Tainted code. I looked into it and coulnd't get HHVM to compile with TAINTED=1. Then I checked and none of the extension functions we exposed about tainting were used in WWW. Scratching my head I asked, @srenfro and @jdelong, who  thought it was dead. So I killed this zombie.
2013-04-12 12:04:04 -07:00

480 linhas
12 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <algorithm>
#include <runtime/base/util/string_buffer.h>
#include <util/alloc.h>
#include <runtime/base/file/file.h>
#include <runtime/base/zend/zend_functions.h>
#include <runtime/base/zend/utf8_decode.h>
#include <runtime/ext/ext_json.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
StringBuffer::StringBuffer(int initialSize /* = 63 */)
: m_initialCap(initialSize), m_maxBytes(kDefaultOutputLimit),
m_len(0) {
assert(initialSize > 0);
m_str = NEW(StringData)(initialSize);
MutableSlice s = m_str->mutableSlice();
m_buffer = s.ptr;
m_cap = s.len;
}
StringBuffer::~StringBuffer() {
if (m_str) {
assert((m_str->setSize(0), true)); // appease StringData::checkSane()
DELETE(StringData)(m_str);
}
}
const char *StringBuffer::data() const {
if (m_buffer && m_len) {
m_buffer[m_len] = '\0'; // fixup
return m_buffer;
}
return nullptr;
}
char StringBuffer::charAt(int pos) const {
assert(pos >= 0 && pos < m_len);
if (m_buffer && pos >= 0 && pos < m_len) {
return m_buffer[pos];
}
return '\0';
}
String StringBuffer::detachImpl() {
if (m_buffer && m_len) {
assert(m_str && m_str->getCount() == 0);
m_buffer[m_len] = '\0'; // fixup
StringData* str = m_str;
str->setSize(m_len);
m_str = 0;
m_buffer = 0;
m_len = 0;
m_cap = 0;
return String(str); // causes incref
}
return String("");
}
String StringBuffer::copy() {
// REGISTER_ACCESSED() is called by data()
return String(data(), size(), CopyString);
}
void StringBuffer::absorb(StringBuffer &buf) {
if (empty()) {
StringData* str = m_str;
m_str = buf.m_str;
m_buffer = buf.m_buffer;
m_len = buf.m_len;
m_cap = buf.m_cap;
buf.m_str = str;
if (str) {
buf.m_buffer = (char*)str->data();
buf.m_len = str->size();
buf.m_cap = str->capacity();
} else {
buf.m_buffer = 0;
buf.m_len = 0;
buf.m_cap = 0;
}
buf.reset();
} else {
// REGISTER_ACCESSED()/REGISTER_MUTATED() are called by append()/detach()
append(buf.detach());
}
}
void StringBuffer::reset() {
m_len = 0;
}
void StringBuffer::release() {
if (m_str) {
m_buffer[m_len] = 0; // appease StringData::checkSane()
DELETE(StringData)(m_str);
}
m_str = 0;
m_buffer = 0;
m_len = m_cap = 0;
}
void StringBuffer::resize(int size) {
assert(size >= 0 && size <= m_cap);
if (size >= 0 && size <= m_cap) {
m_len = size;
}
}
char *StringBuffer::reserve(int size) {
if (!m_buffer) {
m_str = NEW(StringData)(std::max(m_initialCap, m_len + size));
m_buffer = (char*)m_str->data();
m_cap = m_str->capacity();
} else if (m_cap - m_len < size) {
m_buffer[m_len] = 0;
m_str->setSize(m_len);
MutableSlice s = m_str->reserve(m_len + size);
m_buffer = s.ptr;
m_cap = s.len;
}
return m_buffer + m_len;
}
void StringBuffer::append(int n) {
char buf[12];
int is_negative;
int len;
const StringData *sd = String::GetIntegerStringData(n);
char *p;
if (!sd) {
p = conv_10(n, &is_negative, buf + 12, &len);
} else {
p = (char *)sd->data();
len = sd->size();
}
append(p, len);
}
void StringBuffer::append(int64_t n) {
char buf[21];
int is_negative;
int len;
const StringData *sd = String::GetIntegerStringData(n);
char *p;
if (!sd) {
p = conv_10(n, &is_negative, buf + 21, &len);
} else {
p = (char *)sd->data();
len = sd->size();
}
append(p, len);
}
void StringBuffer::append(CVarRef v) {
Variant::TypedValueAccessor tva = v.getTypedAccessor();
if (Variant::IsString(tva)) {
append(Variant::GetAsString(tva));
} else if (IS_INT_TYPE(Variant::GetAccessorType(tva))) {
append(Variant::GetInt64(tva));
} else {
append(v.toString());
}
}
void StringBuffer::appendHelper(char ch) {
if (!m_buffer) reserve(1);
if (m_len == m_cap) {
growBy(1);
}
m_buffer[m_len++] = ch;
}
void StringBuffer::append(CStrRef s) {
// REGISTER_MUTATED() is called by data()
append(s.data(), s.size());
}
void StringBuffer::appendHelper(const char *s, int len) {
if (!m_buffer) reserve(len);
assert(s);
assert(len >= 0);
if (len <= 0) return;
if (len > m_cap - m_len) {
growBy(len);
}
memcpy(m_buffer + m_len, s, len);
m_len += len;
}
#define REVERSE16(us) \
(((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | \
(((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf)) \
void StringBuffer::appendJsonEscape(const char *s, int len, int options) {
if (len == 0) {
append("\"\"", 2);
} else {
static const char digits[] = "0123456789abcdef";
int start = size();
append('"');
UTF8To16Decoder decoder(s, len, options & k_JSON_FB_LOOSE);
for (;;) {
int c = decoder.decode();
if (c == UTF8_END) {
append('"');
break;
}
if (c == UTF8_ERROR) {
// discard the part that has been already decoded.
resize(start);
append("null", 4);
break;
}
assert(c >= 0);
unsigned short us = (unsigned short)c;
switch (us) {
case '"':
if (options & k_JSON_HEX_QUOT) {
append("\\u0022", 6);
} else {
append("\\\"", 2);
}
break;
case '\\': append("\\\\", 2); break;
case '/':
if (options & k_JSON_UNESCAPED_SLASHES) {
append('/');
} else {
append("\\/", 2);
}
break;
case '\b': append("\\b", 2); break;
case '\f': append("\\f", 2); break;
case '\n': append("\\n", 2); break;
case '\r': append("\\r", 2); break;
case '\t': append("\\t", 2); break;
case '<':
if (options & k_JSON_HEX_TAG || options & k_JSON_FB_EXTRA_ESCAPES) {
append("\\u003C", 6);
} else {
append('<');
}
break;
case '>':
if (options & k_JSON_HEX_TAG) {
append("\\u003E", 6);
} else {
append('>');
}
break;
case '&':
if (options & k_JSON_HEX_AMP) {
append("\\u0026", 6);
} else {
append('&');
}
break;
case '\'':
if (options & k_JSON_HEX_APOS) {
append("\\u0027", 6);
} else {
append('\'');
}
break;
case '@':
if (options & k_JSON_FB_EXTRA_ESCAPES) {
append("\\u0040", 6);
} else {
append('@');
}
break;
case '%':
if (options & k_JSON_FB_EXTRA_ESCAPES) {
append("\\u0025", 6);
} else {
append('%');
}
break;
default:
if (us >= ' ' && (us & 127) == us) {
append((char)us);
} else {
append("\\u", 2);
us = REVERSE16(us);
append(digits[us & ((1 << 4) - 1)]); us >>= 4;
append(digits[us & ((1 << 4) - 1)]); us >>= 4;
append(digits[us & ((1 << 4) - 1)]); us >>= 4;
append(digits[us & ((1 << 4) - 1)]);
}
break;
}
}
}
}
void StringBuffer::printf(const char *format, ...) {
va_list ap;
va_start(ap, format);
bool printed = false;
for (int len = 1024; !printed; len <<= 1) {
va_list v;
va_copy(v, ap);
char *buf = (char*)smart_malloc(len);
if (vsnprintf(buf, len, format, v) < len) {
append(buf);
printed = true;
}
smart_free(buf);
va_end(v);
}
va_end(ap);
}
void StringBuffer::read(FILE* in, int page_size /* = 1024 */) {
assert(in);
assert(page_size > 0);
if (!m_buffer) reserve(page_size);
while (true) {
int buffer_size = m_cap - m_len;
if (buffer_size < page_size) {
growBy(page_size);
buffer_size = m_cap - m_len;
}
size_t len = fread(m_buffer + m_len, 1, buffer_size, in);
if (len == 0) break;
m_len += len;
}
}
void StringBuffer::read(File* in, int page_size /* = 1024 */) {
assert(in);
assert(page_size > 0);
if (!m_buffer) reserve(page_size);
while (true) {
int buffer_size = m_cap - m_len;
if (buffer_size < page_size) {
growBy(page_size);
buffer_size = m_cap - m_len;
}
int64_t len = in->readImpl(m_buffer + m_len, buffer_size);
assert(len >= 0);
if (len == 0) break;
m_len += len;
}
}
void StringBuffer::growBy(int spaceRequired) {
/*
* The default initial size is a power-of-two minus 1.
* This doubling scheme keeps the total block size a
* power of two, which should be good for memory allocators.
* But note there is no guarantee either that the initial size
* is power-of-two minus 1, or that it stays that way
* (new_size < minSize below).
*/
long new_size = m_cap * 2L + 1;
long minSize = m_cap + (long)spaceRequired;
if (new_size < minSize) {
new_size = minSize;
}
if (m_maxBytes > 0 && new_size > m_maxBytes) {
if (minSize > m_maxBytes) {
throw StringBufferLimitException(m_maxBytes, detach());
} else {
new_size = m_maxBytes;
}
}
m_buffer[m_len] = 0;
m_str->setSize(m_len);
MutableSlice s = m_str->reserve(new_size);
m_buffer = s.ptr;
m_cap = s.len;
}
CstrBuffer::CstrBuffer(int cap)
: m_buffer((char*)Util::safe_malloc(cap + 1)), m_len(0), m_cap(cap) {
assert(unsigned(cap) <= kMaxCap);
}
CstrBuffer::CstrBuffer(const char *filename)
: m_buffer(nullptr), m_len(0) {
struct stat sb;
if (stat(filename, &sb) == 0) {
if (sb.st_size > kMaxCap - 1) {
std::ostringstream out;
out << "file " << filename << " is too large";
throw StringBufferLimitException(kMaxCap,
String(out.str().c_str()));
}
m_cap = sb.st_size;
m_buffer = (char *)Util::safe_malloc(m_cap + 1);
int fd = ::open(filename, O_RDONLY);
if (fd != -1) {
while (m_len < m_cap) {
int buffer_size = m_cap - m_len;
int len = ::read(fd, m_buffer + m_len, buffer_size);
if (len == -1 && errno == EINTR) continue;
if (len <= 0) break;
m_len += len;
}
::close(fd);
}
}
}
CstrBuffer::CstrBuffer(char* data, int len)
: m_buffer(data), m_len(len), m_cap(len) {
assert(unsigned(len) < kMaxCap);
}
CstrBuffer::~CstrBuffer() {
free(m_buffer);
}
void CstrBuffer::append(const char* data, int len) {
assert(m_buffer && len >= 0);
unsigned newlen = m_len + len;
if (newlen + 1 > m_cap) {
if (newlen + 1 > kMaxCap) {
throw StringBufferLimitException(kMaxCap, detach());
}
unsigned newcap = Util::nextPower2(newlen + 1);
m_buffer = (char*)Util::safe_realloc(m_buffer, newcap);
m_cap = newcap - 1;
assert(newlen + 1 <= m_cap);
}
memcpy(m_buffer + m_len, data, len);
m_buffer[m_len = newlen] = 0;
}
String CstrBuffer::detach() {
assert(m_len <= m_cap);
m_buffer[m_len] = 0;
String s(m_buffer, m_len, AttachString);
m_buffer = 0;
m_len = m_cap = 0;
return s;
}
///////////////////////////////////////////////////////////////////////////////
}