Make abort messages available to debuggerd.

This adds __libc_fatal, cleans up the internal logging code a bit more,
and switches suitable callers over to __libc_fatal. In addition to logging,
__libc_fatal stashes the message somewhere that the debuggerd signal handler
can find it before calling abort.

In the debuggerd signal handler, we pass this address to debuggerd so that
it can come back with ptrace to read the message and present it to the user.

Bug: 8531731

(cherry picked from commit 0d787c1fa1)

Change-Id: I5daeeaa36c1fc23f7f437d73a19808d9d558dd4d
This commit is contained in:
Elliott Hughes 2013-04-04 13:46:46 -07:00
parent 9b84824dfd
commit 7b4d77e400
12 changed files with 247 additions and 281 deletions

View File

@ -32,6 +32,5 @@
#include "libc_logging.h"
void __stack_chk_fail() {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "stack corruption detected");
abort();
__libc_fatal("stack corruption detected");
}

View File

@ -28,27 +28,16 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "libc_logging.h"
// We log to stderr for the benefit of "adb shell" users, and the log for the benefit
// of regular app developers who want to see their asserts.
void __assert(const char* file, int line, const char* failed_expression) {
const char* fmt = "%s:%d: assertion \"%s\" failed\n";
__libc_format_log(ANDROID_LOG_FATAL, "libc", fmt, file, line, failed_expression);
fprintf(stderr, fmt, file, line, failed_expression);
abort();
__libc_fatal("%s:%d: assertion \"%s\" failed", file, line, failed_expression);
/* NOTREACHED */
}
void __assert2(const char* file, int line, const char* function, const char* failed_expression) {
const char* fmt = "%s:%d: %s: assertion \"%s\" failed\n";
__libc_format_log(ANDROID_LOG_FATAL, "libc", fmt, file, line, function, failed_expression);
fprintf(stderr, fmt, file, line, function, failed_expression);
abort();
__libc_fatal("%s:%d: %s: assertion \"%s\" failed", file, line, function, failed_expression);
/* NOTREACHED */
}

View File

@ -16,15 +16,7 @@
#include "dlmalloc.h"
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/ashmem.h>
#include <private/libc_logging.h>
#include "private/libc_logging.h"
// Send dlmalloc errors to the log.
static void __bionic_heap_corruption_error(const char* function);
@ -37,15 +29,12 @@ static void __bionic_heap_usage_error(const char* function, void* address);
#include "../upstream-dlmalloc/malloc.c"
static void __bionic_heap_corruption_error(const char* function) {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "@@@ ABORTING: heap corruption detected by %s",
function);
abort();
__libc_fatal("@@@ ABORTING: heap corruption detected by %s", function);
}
static void __bionic_heap_usage_error(const char* function, void* address) {
__libc_format_log(ANDROID_LOG_FATAL, "libc",
"@@@ ABORTING: invalid address or address of corrupt block %p passed to %s",
address, function);
__libc_fatal("@@@ ABORTING: invalid address or address of corrupt block %p passed to %s",
address, function);
// So that we can get a memory dump around the specific address.
*((int**) 0xdeadbaad) = (int*) address;
}

View File

@ -45,6 +45,7 @@
#include "private/KernelArgumentBlock.h"
#include "pthread_internal.h"
extern "C" abort_msg_t** __abort_message_ptr;
extern "C" unsigned __get_sp(void);
extern "C" int __system_properties_init(void);
@ -96,6 +97,7 @@ void __libc_init_common(KernelArgumentBlock& args) {
errno = 0;
__libc_auxv = args.auxv;
__progname = args.argv[0] ? args.argv[0] : "<unknown>";
__abort_message_ptr = args.abort_message_ptr;
// AT_RANDOM is a pointer to 16 bytes of randomness on the stack.
__stack_chk_guard = *reinterpret_cast<uintptr_t*>(getauxval(AT_RANDOM));

View File

@ -31,228 +31,96 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*** Generic output sink
***/
struct Out {
void *opaque;
void (*send)(void *opaque, const char *data, int len);
};
static void out_send(Out *o, const char *data, size_t len) {
o->send(o->opaque, data, (int)len);
}
static void
out_send_repeat(Out *o, char ch, int count)
{
char pad[8];
const int padSize = (int)sizeof(pad);
memset(pad, ch, sizeof(pad));
while (count > 0) {
int avail = count;
if (avail > padSize) {
avail = padSize;
}
o->send(o->opaque, pad, avail);
count -= avail;
}
}
/* forward declaration */
static void out_vformat(Out* o, const char* format, va_list args);
/*** Bounded buffer output
***/
struct BufOut {
Out out[1];
char *buffer;
char *pos;
char *end;
int total;
};
static void buf_out_send(void *opaque, const char *data, int len) {
BufOut *bo = reinterpret_cast<BufOut*>(opaque);
if (len < 0) {
len = strlen(data);
}
bo->total += len;
while (len > 0) {
int avail = bo->end - bo->pos;
if (avail == 0)
break;
if (avail > len)
avail = len;
memcpy(bo->pos, data, avail);
bo->pos += avail;
bo->pos[0] = '\0';
len -= avail;
}
}
static Out*
buf_out_init(BufOut *bo, char *buffer, size_t size)
{
if (size == 0)
return NULL;
bo->out->opaque = bo;
bo->out->send = buf_out_send;
bo->buffer = buffer;
bo->end = buffer + size - 1;
bo->pos = bo->buffer;
bo->pos[0] = '\0';
bo->total = 0;
return bo->out;
}
static int
buf_out_length(BufOut *bo)
{
return bo->total;
}
static int
vformat_buffer(char *buff, size_t buf_size, const char *format, va_list args)
{
BufOut bo;
Out *out;
out = buf_out_init(&bo, buff, buf_size);
if (out == NULL)
return 0;
out_vformat(out, format, args);
return buf_out_length(&bo);
}
int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
va_list args;
va_start(args, format);
int result = vformat_buffer(buffer, buffer_size, format, args);
va_end(args);
return result;
}
/*** File descriptor output
***/
struct FdOut {
Out out[1];
int fd;
int total;
};
static void
fd_out_send(void *opaque, const char *data, int len)
{
FdOut *fdo = reinterpret_cast<FdOut*>(opaque);
if (len < 0)
len = strlen(data);
while (len > 0) {
int ret = write(fdo->fd, data, len);
if (ret < 0) {
if (errno == EINTR)
continue;
break;
}
data += ret;
len -= ret;
fdo->total += ret;
}
}
static Out*
fd_out_init(FdOut *fdo, int fd)
{
fdo->out->opaque = fdo;
fdo->out->send = fd_out_send;
fdo->fd = fd;
fdo->total = 0;
return fdo->out;
}
static int
fd_out_length(FdOut *fdo)
{
return fdo->total;
}
int __libc_format_fd(int fd, const char* format, ...) {
FdOut fdo;
Out* out = fd_out_init(&fdo, fd);
if (out == NULL) {
return 0;
}
va_list args;
va_start(args, format);
out_vformat(out, format, args);
va_end(args);
return fd_out_length(&fdo);
}
/*** Log output
***/
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <unistd.h>
static pthread_mutex_t gAbortMsgLock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t gLogInitializationLock = PTHREAD_MUTEX_INITIALIZER;
int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) {
char buf[1024];
int buf_strlen = vformat_buffer(buf, sizeof(buf), fmt, args);
__LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common.
static int main_log_fd = -1;
if (main_log_fd == -1) {
ScopedPthreadMutexLocker locker(&gLogInitializationLock);
main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));
if (main_log_fd == -1) {
return -1;
// Must be kept in sync with frameworks/base/core/java/android/util/EventLog.java.
enum AndroidEventLogType {
EVENT_TYPE_INT = 0,
EVENT_TYPE_LONG = 1,
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
};
struct BufferOutputStream {
public:
BufferOutputStream(char* buffer, size_t size) : total(0) {
buffer_ = buffer;
end_ = buffer + size - 1;
pos_ = buffer_;
pos_[0] = '\0';
}
~BufferOutputStream() {
}
void Send(const char* data, int len) {
if (len < 0) {
len = strlen(data);
}
while (len > 0) {
int avail = end_ - pos_;
if (avail == 0) {
break;
}
if (avail > len) {
avail = len;
}
memcpy(pos_, data, avail);
pos_ += avail;
pos_[0] = '\0';
len -= avail;
total += avail;
}
}
struct iovec vec[3];
vec[0].iov_base = &priority;
vec[0].iov_len = 1;
vec[1].iov_base = const_cast<char*>(tag);
vec[1].iov_len = strlen(tag) + 1;
vec[2].iov_base = const_cast<char*>(buf);
vec[2].iov_len = buf_strlen + 1;
int total;
return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
}
private:
char* buffer_;
char* pos_;
char* end_;
};
int __libc_format_log(int priority, const char* tag, const char* format, ...) {
va_list args;
va_start(args, format);
int result = __libc_format_log_va_list(priority, tag, format, args);
va_end(args);
return result;
}
struct FdOutputStream {
public:
FdOutputStream(int fd) : total(0), fd_(fd) {
}
void Send(const char* data, int len) {
if (len < 0) {
len = strlen(data);
}
while (len > 0) {
int rc = TEMP_FAILURE_RETRY(write(fd_, data, len));
if (rc == -1) {
break;
}
data += rc;
len -= rc;
total += rc;
}
}
int total;
private:
int fd_;
};
/*** formatted output implementation
***/
@ -263,9 +131,7 @@ int __libc_format_log(int priority, const char* tag, const char* format, ...) {
*
* NOTE: Does *not* handle a sign prefix.
*/
static unsigned
parse_decimal(const char *format, int *ppos)
{
static unsigned parse_decimal(const char *format, int *ppos) {
const char* p = format + *ppos;
unsigned result = 0;
@ -273,8 +139,9 @@ parse_decimal(const char *format, int *ppos)
int ch = *p;
unsigned d = (unsigned)(ch - '0');
if (d >= 10U)
if (d >= 10U) {
break;
}
result = result*10 + d;
p++;
@ -341,10 +208,25 @@ static void format_integer(char* buf, size_t buf_size, uint64_t value, char conv
format_unsigned(buf, buf_size, value, base, caps);
}
template <typename Out>
static void SendRepeat(Out& o, char ch, int count) {
char pad[8];
memset(pad, ch, sizeof(pad));
const int pad_size = static_cast<int>(sizeof(pad));
while (count > 0) {
int avail = count;
if (avail > pad_size) {
avail = pad_size;
}
o.Send(pad, avail);
count -= avail;
}
}
/* Perform formatted output to an output target 'o' */
static void
out_vformat(Out *o, const char *format, va_list args)
{
template <typename Out>
static void out_vformat(Out& o, const char* format, va_list args) {
int nn = 0;
for (;;) {
@ -371,7 +253,7 @@ out_vformat(Out *o, const char *format, va_list args)
} while (1);
if (mm > nn) {
out_send(o, format+nn, mm-nn);
o.Send(format+nn, mm-nn);
nn = mm;
}
@ -387,7 +269,7 @@ out_vformat(Out *o, const char *format, va_list args)
c = format[nn++];
if (c == '\0') { /* single trailing '%' ? */
c = '%';
out_send(o, &c, 1);
o.Send(&c, 1);
return;
}
else if (c == '0') {
@ -508,28 +390,74 @@ out_vformat(Out *o, const char *format, va_list args)
if (slen < width && !padLeft) {
char padChar = padZero ? '0' : ' ';
out_send_repeat(o, padChar, width - slen);
SendRepeat(o, padChar, width - slen);
}
out_send(o, str, slen);
o.Send(str, slen);
if (slen < width && padLeft) {
char padChar = padZero ? '0' : ' ';
out_send_repeat(o, padChar, width - slen);
SendRepeat(o, padChar, width - slen);
}
}
}
// must be kept in sync with frameworks/base/core/java/android/util/EventLog.java
enum AndroidEventLogType {
EVENT_TYPE_INT = 0,
EVENT_TYPE_LONG = 1,
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
};
int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
BufferOutputStream os(buffer, buffer_size);
va_list args;
va_start(args, format);
out_vformat(os, format, args);
va_end(args);
return os.total;
}
int __libc_format_fd(int fd, const char* format, ...) {
FdOutputStream os(fd);
va_list args;
va_start(args, format);
out_vformat(os, format, args);
va_end(args);
return os.total;
}
static int __libc_write_log(int priority, const char* tag, const char* msg) {
static int main_log_fd = -1;
if (main_log_fd == -1) {
ScopedPthreadMutexLocker locker(&gLogInitializationLock);
main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));
if (main_log_fd == -1) {
return -1;
}
}
iovec vec[3];
vec[0].iov_base = &priority;
vec[0].iov_len = 1;
vec[1].iov_base = const_cast<char*>(tag);
vec[1].iov_len = strlen(tag) + 1;
vec[2].iov_base = const_cast<char*>(msg);
vec[2].iov_len = strlen(msg) + 1;
return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
}
int __libc_format_log_va_list(int priority, const char* tag, const char* format, va_list args) {
char buffer[1024];
BufferOutputStream os(buffer, sizeof(buffer));
out_vformat(os, format, args);
return __libc_write_log(priority, tag, buffer);
}
int __libc_format_log(int priority, const char* tag, const char* format, ...) {
va_list args;
va_start(args, format);
int result = __libc_format_log_va_list(priority, tag, format, args);
va_end(args);
return result;
}
static int __libc_android_log_event(int32_t tag, char type, const void* payload, size_t len) {
struct iovec vec[3];
iovec vec[3];
vec[0].iov_base = &tag;
vec[0].iov_len = sizeof(tag);
vec[1].iov_base = &type;
@ -554,9 +482,45 @@ void __libc_android_log_event_uid(int32_t tag) {
}
void __fortify_chk_fail(const char *msg, uint32_t tag) {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "FORTIFY_SOURCE: %s. Calling abort().\n", msg);
if (tag != 0) {
__libc_android_log_event_uid(tag);
}
__libc_fatal("FORTIFY_SOURCE: %s. Calling abort().", msg);
}
void __libc_fatal(const char* format, ...) {
char msg[1024];
BufferOutputStream os(msg, sizeof(msg));
va_list args;
va_start(args, format);
out_vformat(os, format, args);
va_end(args);
// TODO: log to stderr for the benefit of "adb shell" users.
// Log to the log for the benefit of regular app developers (whose stdout and stderr are closed).
__libc_write_log(ANDROID_LOG_FATAL, "libc", msg);
__libc_set_abort_message(msg);
abort();
}
void __libc_set_abort_message(const char* msg) {
size_t size = sizeof(abort_msg_t) + strlen(msg) + 1;
void* map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (map == MAP_FAILED) {
return;
}
if (__abort_message_ptr != NULL) {
ScopedPthreadMutexLocker locker(&gAbortMsgLock);
if (*__abort_message_ptr != NULL) {
munmap(*__abort_message_ptr, (*__abort_message_ptr)->size);
}
abort_msg_t* new_abort_message = reinterpret_cast<abort_msg_t*>(map);
new_abort_message->size = size;
strcpy(new_abort_message->msg, msg);
*__abort_message_ptr = new_abort_message;
}
}

View File

@ -600,7 +600,7 @@ int malloc_debug_initialize() {
error_log("Unable to open /dev/qemu_trace");
return -1;
} else {
qtrace = mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
qtrace = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if (qtrace == MAP_FAILED) {

View File

@ -106,7 +106,7 @@ int __system_properties_init(void)
goto cleanup;
}
prop_area *pa = mmap(0, fd_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
prop_area *pa = mmap(NULL, fd_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (pa == MAP_FAILED) {
goto cleanup;
@ -150,7 +150,7 @@ const prop_info *__system_property_find(const char *name)
while(count--) {
unsigned entry = *toc++;
if(TOC_NAME_LEN(entry) != len) continue;
pi = TOC_TO_INFO(pa, entry);
if(memcmp(name, pi->name, len)) continue;
@ -163,7 +163,7 @@ const prop_info *__system_property_find(const char *name)
int __system_property_read(const prop_info *pi, char *name, char *value)
{
unsigned serial, len;
for(;;) {
serial = pi->serial;
while(SERIAL_DIRTY(serial)) {

View File

@ -21,6 +21,8 @@
#include <stdint.h>
#include <sys/auxv.h>
struct abort_msg_t;
// When the kernel starts the dynamic linker, it passes a pointer to a block
// of memory containing argc, the argv array, the environment variable array,
// and the array of ELF aux vectors. This class breaks that block up into its
@ -67,6 +69,8 @@ class KernelArgumentBlock {
char** envp;
Elf32_auxv_t* auxv;
abort_msg_t** abort_message_ptr;
private:
// Disallow copy and assignment.
KernelArgumentBlock(const KernelArgumentBlock&);

View File

@ -67,6 +67,20 @@ enum {
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
};
struct abort_msg_t {
size_t size;
char msg[0];
};
__LIBC_HIDDEN__ void __libc_set_abort_message(const char* msg);
//
// Formats a message to the log (priority 'fatal'), then aborts.
//
__LIBC_HIDDEN__ __noreturn void __libc_fatal(const char* format, ...)
__attribute__((__format__(printf, 1, 2)));
//
// Formatting routines for the C library's internal debugging.
// Unlike the usual alternatives, these don't allocate.

View File

@ -52,8 +52,12 @@ enum debugger_action_t {
/* message sent over the socket */
struct debugger_msg_t {
debugger_action_t action;
pid_t tid;
// version 1 included:
debugger_action_t action;
pid_t tid;
// version 2 added:
uintptr_t abort_msg_address;
};
// see man(2) prctl, specifically the section about PR_GET_NAME
@ -154,14 +158,14 @@ static bool haveSiginfo(int signum) {
sigemptyset(&newact.sa_mask);
if (sigaction(signum, &newact, &oldact) < 0) {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed testing for SA_SIGINFO: %s",
__libc_format_log(ANDROID_LOG_WARN, "libc", "Failed testing for SA_SIGINFO: %s",
strerror(errno));
return 0;
return false;
}
bool ret = (oldact.sa_flags & SA_SIGINFO) != 0;
if (sigaction(signum, &oldact, NULL) == -1) {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "Restore failed in test for SA_SIGINFO: %s",
__libc_format_log(ANDROID_LOG_WARN, "libc", "Restore failed in test for SA_SIGINFO: %s",
strerror(errno));
}
return ret;
@ -186,19 +190,17 @@ void debuggerd_signal_handler(int n, siginfo_t* info, void*) {
int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);
if (s >= 0) {
/* debugger knows our pid from the credentials on the
* local socket but we need to tell it our tid. It
* is paranoid and will verify that we are giving a tid
* that's actually in our process
*/
int ret;
// debuggerd knows our pid from the credentials on the
// local socket but we need to tell it the tid of the crashing thread.
// debuggerd will be paranoid and verify that we sent a tid
// that's actually in our process.
debugger_msg_t msg;
msg.action = DEBUGGER_ACTION_CRASH;
msg.tid = tid;
ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
msg.abort_msg_address = reinterpret_cast<uintptr_t>(gAbortMessage);
int ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
if (ret == sizeof(msg)) {
/* if the write failed, there is no point to read on
* the file descriptor. */
// if the write failed, there is no point trying to read a response.
ret = TEMP_FAILURE_RETRY(read(s, &tid, 1));
int saved_errno = errno;
notify_gdb_of_libraries();

View File

@ -105,6 +105,8 @@ static soinfo* gLdPreloads[LDPRELOAD_MAX + 1];
__LIBC_HIDDEN__ int gLdDebugVerbosity;
__LIBC_HIDDEN__ abort_msg_t* gAbortMessage = NULL; // For debuggerd.
enum RelocationKind {
kRelocAbsolute = 0,
kRelocRelative,
@ -171,8 +173,7 @@ size_t linker_get_error_buffer_size() {
*/
extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity();
static r_debug _r_debug = {1, NULL, &rtld_db_dlactivity,
RT_CONSISTENT, 0};
static r_debug _r_debug = {1, NULL, &rtld_db_dlactivity, RT_CONSISTENT, 0};
static link_map_t* r_debug_tail = 0;
static pthread_mutex_t gDebugMutex = PTHREAD_MUTEX_INITIALIZER;
@ -1815,8 +1816,8 @@ extern "C" Elf32_Addr __linker_init(void* raw_args) {
Elf32_Addr linker_addr = args.getauxval(AT_BASE);
Elf32_Ehdr *elf_hdr = (Elf32_Ehdr*) linker_addr;
Elf32_Phdr *phdr = (Elf32_Phdr*)((unsigned char*) linker_addr + elf_hdr->e_phoff);
Elf32_Ehdr* elf_hdr = (Elf32_Ehdr*) linker_addr;
Elf32_Phdr* phdr = (Elf32_Phdr*)((unsigned char*) linker_addr + elf_hdr->e_phoff);
soinfo linker_so;
memset(&linker_so, 0, sizeof(soinfo));
@ -1841,6 +1842,7 @@ extern "C" Elf32_Addr __linker_init(void* raw_args) {
// We have successfully fixed our own relocations. It's safe to run
// the main part of the linker now.
args.abort_message_ptr = &gAbortMessage;
Elf32_Addr start_address = __linker_init_post_relocation(args, linker_addr);
set_soinfo_pool_protection(PROT_READ);

View File

@ -186,6 +186,7 @@ Elf32_Sym* dladdr_find_symbol(soinfo* si, const void* addr);
Elf32_Sym* dlsym_handle_lookup(soinfo* si, const char* name);
void debuggerd_init();
extern "C" abort_msg_t* gAbortMessage;
extern "C" void notify_gdb_of_libraries();
char* linker_get_error_buffer();