Skip to content

Commit

Permalink
[scudo][standalone] Add string utility functions
Browse files Browse the repository at this point in the history
Summary:
Add some string utility functions, notably to format strings, get
lengths, convert a string to a number. Those functions will be
used in reports and flags (coming up next). They were mostly borrowed
from sanitizer_common.

Make use of the string length function in a couple places in the
platform code that was checked in with inlined version of it.

Add some tests.

Reviewers: morehouse, eugenis, vitalybuka, hctim

Reviewed By: morehouse, vitalybuka

Subscribers: mgorny, delcypher, jdoerfert, #sanitizers, llvm-commits

Tags: #llvm, #sanitizers

Differential Revision: https://reviews.llvm.org/D59262

llvm-svn: 356457
  • Loading branch information
Kostya Kortchinsky committed Mar 19, 2019
1 parent 8ee477a commit 7045c6f
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 13 deletions.
4 changes: 3 additions & 1 deletion compiler-rt/lib/scudo/standalone/CMakeLists.txt
Expand Up @@ -38,7 +38,8 @@ set(SCUDO_SOURCES
crc32_hw.cc
common.cc
fuchsia.cc
linux.cc)
linux.cc
string_utils.cc)

# Enable the SSE 4.2 instruction set for crc32_hw.cc, if available.
if (COMPILER_RT_HAS_MSSE4_2_FLAG)
Expand All @@ -61,6 +62,7 @@ set(SCUDO_HEADERS
mutex.h
platform.h
stats.h
string_utils.h
vector.h)

if(COMPILER_RT_HAS_SCUDO_STANDALONE)
Expand Down
11 changes: 3 additions & 8 deletions compiler-rt/lib/scudo/standalone/fuchsia.cc
Expand Up @@ -12,6 +12,7 @@

#include "common.h"
#include "mutex.h"
#include "string_utils.h"

#include <limits.h> // for PAGE_SIZE
#include <stdlib.h> // for abort()
Expand Down Expand Up @@ -110,10 +111,7 @@ void *map(void *Addr, uptr Size, const char *Name, uptr Flags, u64 *Extra) {
dieOnMapUnmapError(Status == ZX_ERR_NO_MEMORY);
return nullptr;
}
uptr N = 0;
while (Name[N])
N++;
_zx_object_set_property(Vmo, ZX_PROP_NAME, Name, N);
_zx_object_set_property(Vmo, ZX_PROP_NAME, Name, strlen(Name));
}

uintptr_t P;
Expand Down Expand Up @@ -203,10 +201,7 @@ bool getRandom(void *Buffer, uptr Length, bool Blocking) {
}

void outputRaw(const char *Buffer) {
uptr N = 0;
while (Buffer[N])
N++;
__sanitizer_log_write(Buffer, N);
__sanitizer_log_write(Buffer, strlen(Buffer));
}

void setAbortMessage(const char *Message) {}
Expand Down
6 changes: 2 additions & 4 deletions compiler-rt/lib/scudo/standalone/linux.cc
Expand Up @@ -13,6 +13,7 @@
#include "common.h"
#include "linux.h"
#include "mutex.h"
#include "string_utils.h"

#include <errno.h>
#include <fcntl.h>
Expand Down Expand Up @@ -133,10 +134,7 @@ bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {
void outputRaw(const char *Buffer) {
static StaticSpinMutex Mutex;
SpinMutexLock L(&Mutex);
uptr N = 0;
while (Buffer[N])
N++;
write(2, Buffer, N);
write(2, Buffer, strlen(Buffer));
}

extern "C" WEAK void android_set_abort_message(const char *);
Expand Down
236 changes: 236 additions & 0 deletions compiler-rt/lib/scudo/standalone/string_utils.cc
@@ -0,0 +1,236 @@
//===-- string_utils.cc -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "string_utils.h"
#include "common.h"

#include <ctype.h>
#include <stdarg.h>
#include <string.h>

namespace scudo {

static int appendChar(char **Buffer, const char *BufferEnd, char C) {
if (*Buffer < BufferEnd) {
**Buffer = C;
(*Buffer)++;
}
return 1;
}

// Appends number in a given Base to buffer. If its length is less than
// |MinNumberLength|, it is padded with leading zeroes or spaces, depending
// on the value of |PadWithZero|.
static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
u8 Base, u8 MinNumberLength, bool PadWithZero,
bool Negative, bool Upper) {
constexpr uptr MaxLen = 30;
RAW_CHECK(Base == 10 || Base == 16);
RAW_CHECK(Base == 10 || !Negative);
RAW_CHECK(AbsoluteValue || !Negative);
RAW_CHECK(MinNumberLength < MaxLen);
int Res = 0;
if (Negative && MinNumberLength)
--MinNumberLength;
if (Negative && PadWithZero)
Res += appendChar(Buffer, BufferEnd, '-');
uptr NumBuffer[MaxLen];
int Pos = 0;
do {
RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
"appendNumber buffer overflow");
NumBuffer[Pos++] = AbsoluteValue % Base;
AbsoluteValue /= Base;
} while (AbsoluteValue > 0);
if (Pos < MinNumberLength) {
memset(&NumBuffer[Pos], 0,
sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
Pos = MinNumberLength;
}
RAW_CHECK(Pos > 0);
Pos--;
for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
char c = (PadWithZero || Pos == 0) ? '0' : ' ';
Res += appendChar(Buffer, BufferEnd, c);
}
if (Negative && !PadWithZero)
Res += appendChar(Buffer, BufferEnd, '-');
for (; Pos >= 0; Pos--) {
char Digit = static_cast<char>(NumBuffer[Pos]);
Digit = static_cast<char>((Digit < 10) ? '0' + Digit
: (Upper ? 'A' : 'a') + Digit - 10);
Res += appendChar(Buffer, BufferEnd, Digit);
}
return Res;
}

static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
u8 Base, u8 MinNumberLength, bool PadWithZero,
bool Upper) {
return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
PadWithZero, /*Negative=*/false, Upper);
}

static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
u8 MinNumberLength, bool PadWithZero) {
const bool Negative = (Num < 0);
return appendNumber(Buffer, BufferEnd,
static_cast<u64>(Negative ? -Num : Num), 10,
MinNumberLength, PadWithZero, Negative,
/*Upper=*/false);
}

// Use the fact that explicitly requesting 0 Width (%0s) results in UB and
// interpret Width == 0 as "no Width requested":
// Width == 0 - no Width requested
// Width < 0 - left-justify S within and pad it to -Width chars, if necessary
// Width > 0 - right-justify S, not implemented yet
static int appendString(char **Buffer, const char *BufferEnd, int Width,
int MaxChars, const char *S) {
if (!S)
S = "<null>";
int Res = 0;
for (; *S; S++) {
if (MaxChars >= 0 && Res >= MaxChars)
break;
Res += appendChar(Buffer, BufferEnd, *S);
}
// Only the left justified strings are supported.
while (Width < -Res)
Res += appendChar(Buffer, BufferEnd, ' ');
return Res;
}

static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
int Res = 0;
Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
/*Upper=*/false);
return Res;
}

int formatString(char *Buffer, uptr BufferLength, const char *Format,
va_list Args) {
UNUSED static const char *PrintfFormatsHelp =
"Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
"%[-]([0-9]*)?(\\.\\*)?s; %c\n";
RAW_CHECK(Format);
RAW_CHECK(BufferLength > 0);
const char *BufferEnd = &Buffer[BufferLength - 1];
const char *Cur = Format;
int Res = 0;
for (; *Cur; Cur++) {
if (*Cur != '%') {
Res += appendChar(&Buffer, BufferEnd, *Cur);
continue;
}
Cur++;
const bool LeftJustified = *Cur == '-';
if (LeftJustified)
Cur++;
bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
const bool PadWithZero = (*Cur == '0');
u8 Width = 0;
if (HaveWidth) {
while (*Cur >= '0' && *Cur <= '9')
Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
}
const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
int Precision = -1;
if (HavePrecision) {
Cur += 2;
Precision = va_arg(Args, int);
}
const bool HaveZ = (*Cur == 'z');
Cur += HaveZ;
const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
Cur += HaveLL * 2;
s64 DVal;
u64 UVal;
const bool HaveLength = HaveZ || HaveLL;
const bool HaveFlags = HaveWidth || HaveLength;
// At the moment only %s supports precision and left-justification.
CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
switch (*Cur) {
case 'd': {
DVal = HaveLL ? va_arg(Args, s64)
: HaveZ ? va_arg(Args, sptr) : va_arg(Args, int);
Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
break;
}
case 'u':
case 'x':
case 'X': {
UVal = HaveLL ? va_arg(Args, u64)
: HaveZ ? va_arg(Args, uptr) : va_arg(Args, unsigned);
const bool Upper = (*Cur == 'X');
Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
Width, PadWithZero, Upper);
break;
}
case 'p': {
RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
break;
}
case 's': {
RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
// Only left-justified Width is supported.
CHECK(!HaveWidth || LeftJustified);
Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
Precision, va_arg(Args, char *));
break;
}
case 'c': {
RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
Res +=
appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
break;
}
case '%': {
RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
Res += appendChar(&Buffer, BufferEnd, '%');
break;
}
default: {
RAW_CHECK_MSG(false, PrintfFormatsHelp);
}
}
}
RAW_CHECK(Buffer <= BufferEnd);
appendChar(&Buffer, BufferEnd + 1, '\0');
return Res;
}

void ScopedString::append(const char *Format, va_list Args) {
CHECK_LT(Length, String.size());
formatString(String.data() + Length, String.size() - Length, Format, Args);
Length += strlen(String.data() + Length);
CHECK_LT(Length, String.size());
}

FORMAT(2, 3)
void ScopedString::append(const char *Format, ...) {
va_list Args;
va_start(Args, Format);
append(Format, Args);
va_end(Args);
}

FORMAT(1, 2)
void Printf(const char *Format, ...) {
va_list Args;
va_start(Args, Format);
ScopedString Msg(512);
Msg.append(Format, Args);
outputRaw(Msg.data());
va_end(Args);
}

} // namespace scudo
42 changes: 42 additions & 0 deletions compiler-rt/lib/scudo/standalone/string_utils.h
@@ -0,0 +1,42 @@
//===-- string_utils.h ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_STRING_UTILS_H_
#define SCUDO_STRING_UTILS_H_

#include "internal_defs.h"
#include "vector.h"

#include <stdarg.h>

namespace scudo {

class ScopedString {
public:
explicit ScopedString(uptr MaxLength) : String(MaxLength), Length(0) {
String[0] = '\0';
}
uptr length() { return Length; }
const char *data() { return String.data(); }
void clear() {
String[0] = '\0';
Length = 0;
}
void append(const char *Format, va_list Args);
void append(const char *Format, ...);

private:
Vector<char> String;
uptr Length;
};

void Printf(const char *Format, ...);

} // namespace scudo

#endif // SCUDO_STRING_UTILS_H_
1 change: 1 addition & 0 deletions compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt
Expand Up @@ -56,6 +56,7 @@ set(SCUDO_UNIT_TEST_SOURCES
map_test.cc
mutex_test.cc
stats_test.cc
strings_test.cc
vector_test.cc
scudo_unit_test_main.cc)

Expand Down

0 comments on commit 7045c6f

Please sign in to comment.