diff options
88 files changed, 6074 insertions, 760 deletions
diff --git a/adb/adb_client.c b/adb/adb_client.c index 8340738..f7823a8 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -278,7 +278,9 @@ int adb_connect(const char *service) return 0; fd = _adb_connect(service); - if(fd == -2) { + if(fd == -1) { + fprintf(stderr,"error: %s\n", __adb_error); + } else if(fd == -2) { fprintf(stderr,"** daemon still not running\n"); } D("adb_connect: return fd %d\n", fd); diff --git a/adb/commandline.c b/adb/commandline.c index 27a1754..c9bb437 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -767,7 +767,7 @@ static int restore(int argc, char** argv) { fd = adb_connect("restore:"); if (fd < 0) { - fprintf(stderr, "adb: unable to connect for backup\n"); + fprintf(stderr, "adb: unable to connect for restore\n"); adb_close(tarFd); return -1; } diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c index 354d0fb..9fec081 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -642,8 +642,8 @@ static int local_build_list(copyinfo **filelist, ci = mkcopyinfo(lpath, rpath, name, 0); if(lstat(ci->src, &st)) { fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno)); + free(ci); closedir(d); - return -1; } if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c index d3e841b..f24f14c 100644 --- a/adb/file_sync_service.c +++ b/adb/file_sync_service.c @@ -110,6 +110,7 @@ static int do_list(int s, const char *path) if(writex(s, &msg.dent, sizeof(msg.dent)) || writex(s, de->d_name, len)) { + closedir(d); return -1; } } diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c index 20c08d2..fa7fd98 100644 --- a/adb/framebuffer_service.c +++ b/adb/framebuffer_service.c @@ -55,13 +55,13 @@ struct fbinfo { void framebuffer_service(int fd, void *cookie) { struct fbinfo fbinfo; - unsigned int i; + unsigned int i, bsize; char buf[640]; int fd_screencap; int w, h, f; int fds[2]; - if (pipe(fds) < 0) goto done; + if (pipe(fds) < 0) goto pipefail; pid_t pid = fork(); if (pid < 0) goto done; @@ -164,17 +164,19 @@ void framebuffer_service(int fd, void *cookie) if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done; /* write data */ - for(i = 0; i < fbinfo.size; i += sizeof(buf)) { - if(readx(fd_screencap, buf, sizeof(buf))) goto done; - if(writex(fd, buf, sizeof(buf))) goto done; + for(i = 0; i < fbinfo.size; i += bsize) { + bsize = sizeof(buf); + if (i + bsize > fbinfo.size) + bsize = fbinfo.size - i; + if(readx(fd_screencap, buf, bsize)) goto done; + if(writex(fd, buf, bsize)) goto done; } - if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done; - if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done; done: TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); close(fds[0]); close(fds[1]); +pipefail: close(fd); } diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index 68bb232..19b3022 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -155,7 +155,10 @@ #define VENDOR_ID_QISDA 0x1D45 // ECS's USB Vendor ID #define VENDOR_ID_ECS 0x03fc - +// MSI's USB Vendor ID +#define VENDOR_ID_MSI 0x0DB0 +// Wacom's USB Vendor ID +#define VENDOR_ID_WACOM 0x0531 /** built-in vendor list */ int builtInVendorIds[] = { @@ -219,6 +222,8 @@ int builtInVendorIds[] = { VENDOR_ID_NOOK, VENDOR_ID_QISDA, VENDOR_ID_ECS, + VENDOR_ID_MSI, + VENDOR_ID_WACOM, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index 3569e27..7d3740c 100644 --- a/cpio/mkbootfs.c +++ b/cpio/mkbootfs.c @@ -220,6 +220,8 @@ static void _archive_dir(char *in, char *out, int ilen, int olen) free(names[i]); } free(names); + + closedir(d); } static void _archive(char *in, char *out, int ilen, int olen) diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 8621e9c..2fe7c7a 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -24,11 +24,11 @@ LOCAL_CFLAGS += -DWITH_VFP_D32 endif # ARCH_ARM_HAVE_VFP_D32 LOCAL_SHARED_LIBRARIES := \ + libbacktrace \ + libc \ libcutils \ liblog \ - libc \ - libcorkscrew \ - libselinux + libselinux \ include $(BUILD_EXECUTABLE) diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index 67e3028..6bcd07e 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -42,7 +42,7 @@ #endif #endif -static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { +static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ char ascii_buffer[32]; /* actual 16 + 1 == 17 */ uintptr_t p, end; @@ -102,7 +102,7 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { p += 4; } *asc_out = '\0'; - _LOG(log, scopeFlags, " %s %s\n", code_buffer, ascii_buffer); + _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); } } @@ -110,16 +110,13 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { * If configured to do so, dump memory around *all* registers * for the crashing thread. */ -void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { struct pt_regs regs; if(ptrace(PTRACE_GETREGS, tid, 0, ®s)) { return; } - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - - if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { + if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; for (int reg = 0; reg < 14; reg++) { @@ -134,39 +131,36 @@ void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), continue; } - _LOG(log, scopeFlags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scopeFlags | SCOPE_SENSITIVE); + _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); } } /* explicitly allow upload of code dump logging */ - _LOG(log, scopeFlags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)regs.ARM_pc, scopeFlags); + _LOG(log, scope_flags, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)regs.ARM_pc, scope_flags); if (regs.ARM_pc != regs.ARM_lr) { - _LOG(log, scopeFlags, "\ncode around lr:\n"); - dump_memory(log, tid, (uintptr_t)regs.ARM_lr, scopeFlags); + _LOG(log, scope_flags, "\ncode around lr:\n"); + dump_memory(log, tid, (uintptr_t)regs.ARM_lr, scope_flags); } } -void dump_registers(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) +void dump_registers(log_t* log, pid_t tid, int scope_flags) { struct pt_regs r; - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); return; } - _LOG(log, scopeFlags, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + _LOG(log, scope_flags, " r0 %08x r1 %08x r2 %08x r3 %08x\n", (uint32_t)r.ARM_r0, (uint32_t)r.ARM_r1, (uint32_t)r.ARM_r2, (uint32_t)r.ARM_r3); - _LOG(log, scopeFlags, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + _LOG(log, scope_flags, " r4 %08x r5 %08x r6 %08x r7 %08x\n", (uint32_t)r.ARM_r4, (uint32_t)r.ARM_r5, (uint32_t)r.ARM_r6, (uint32_t)r.ARM_r7); - _LOG(log, scopeFlags, " r8 %08x r9 %08x sl %08x fp %08x\n", + _LOG(log, scope_flags, " r8 %08x r9 %08x sl %08x fp %08x\n", (uint32_t)r.ARM_r8, (uint32_t)r.ARM_r9, (uint32_t)r.ARM_r10, (uint32_t)r.ARM_fp); - _LOG(log, scopeFlags, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + _LOG(log, scope_flags, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", (uint32_t)r.ARM_ip, (uint32_t)r.ARM_sp, (uint32_t)r.ARM_lr, (uint32_t)r.ARM_pc, (uint32_t)r.ARM_cpsr); @@ -175,14 +169,14 @@ void dump_registers(const ptrace_context_t* context __attribute((unused)), int i; if(ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); return; } for (i = 0; i < NUM_VFP_REGS; i += 2) { - _LOG(log, scopeFlags, " d%-2d %016llx d%-2d %016llx\n", + _LOG(log, scope_flags, " d%-2d %016llx d%-2d %016llx\n", i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); } - _LOG(log, scopeFlags, " scr %08lx\n", vfp_regs.fpscr); + _LOG(log, scope_flags, " scr %08lx\n", vfp_regs.fpscr); #endif } diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c index f42f24c..aa7a3c2 100644 --- a/debuggerd/backtrace.c +++ b/debuggerd/backtrace.c @@ -27,13 +27,11 @@ #include <sys/types.h> #include <sys/ptrace.h> -#include <corkscrew/backtrace.h> +#include <backtrace/backtrace.h> -#include "tombstone.h" +#include "backtrace.h" #include "utility.h" -#define STACK_DEPTH 32 - static void dump_process_header(log_t* log, pid_t pid) { char path[PATH_MAX]; char procnamebuf[1024]; @@ -62,7 +60,7 @@ static void dump_process_footer(log_t* log, pid_t pid) { _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); } -static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool attached, +static void dump_thread(log_t* log, pid_t tid, bool attached, bool* detach_failed, int* total_sleep_time_usec) { char path[PATH_MAX]; char threadnamebuf[1024]; @@ -91,20 +89,12 @@ static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool a wait_for_stop(tid, total_sleep_time_usec); - backtrace_frame_t backtrace[STACK_DEPTH]; - ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); - if (frames <= 0) { - _LOG(log, SCOPE_AT_FAULT, "Could not obtain stack trace for thread.\n"); + backtrace_context_t context; + if (!backtrace_create_context(&context, tid, -1, 0)) { + _LOG(log, SCOPE_AT_FAULT, "Could not create backtrace context.\n"); } else { - backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; - get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); - for (size_t i = 0; i < (size_t)frames; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - _LOG(log, SCOPE_AT_FAULT, " %s\n", line); - } - free_backtrace_symbols(backtrace_symbols, frames); + dump_backtrace_to_log(&context, log, SCOPE_AT_FAULT, " "); + backtrace_destroy_context(&context); } if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { @@ -120,9 +110,8 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, log.amfd = amfd; log.quiet = true; - ptrace_context_t* context = load_ptrace_context(tid); dump_process_header(&log, pid); - dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec); + dump_thread(&log, tid, true, detach_failed, total_sleep_time_usec); char task_path[64]; snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); @@ -140,11 +129,19 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, continue; } - dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec); + dump_thread(&log, new_tid, false, detach_failed, total_sleep_time_usec); } closedir(d); } dump_process_footer(&log, pid); - free_ptrace_context(context); +} + +void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log, + int scope_flags, const char* prefix) { + char buf[512]; + for (size_t i = 0; i < context->backtrace->num_frames; i++) { + backtrace_format_frame_data(context, i, buf, sizeof(buf)); + _LOG(log, scope_flags, "%s%s\n", prefix, buf); + } } diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h index c5c786a..54a60b2 100644 --- a/debuggerd/backtrace.h +++ b/debuggerd/backtrace.h @@ -21,11 +21,17 @@ #include <stdbool.h> #include <sys/types.h> -#include <corkscrew/ptrace.h> +#include <backtrace/backtrace.h> + +#include "utility.h" /* Dumps a backtrace using a format similar to what Dalvik uses so that the result * can be intermixed in a bug report. */ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, int* total_sleep_time_usec); +/* Dumps the backtrace in the backtrace data structure to the log. */ +void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log, + int scope_flags, const char* prefix); + #endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/machine.h b/debuggerd/machine.h index 1619dd3..2f1e201 100644 --- a/debuggerd/machine.h +++ b/debuggerd/machine.h @@ -17,15 +17,11 @@ #ifndef _DEBUGGERD_MACHINE_H #define _DEBUGGERD_MACHINE_H -#include <stddef.h> -#include <stdbool.h> #include <sys/types.h> -#include <corkscrew/ptrace.h> - #include "utility.h" -void dump_memory_and_code(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault); -void dump_registers(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault); +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags); +void dump_registers(log_t* log, pid_t tid, int scope_flags); #endif // _DEBUGGERD_MACHINE_H diff --git a/debuggerd/mips/machine.c b/debuggerd/mips/machine.c index 65fdf02..e06a50c 100644 --- a/debuggerd/mips/machine.c +++ b/debuggerd/mips/machine.c @@ -36,7 +36,7 @@ #define R(x) ((unsigned int)(x)) -static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { +static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) { char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ char ascii_buffer[32]; /* actual 16 + 1 == 17 */ uintptr_t p, end; @@ -92,7 +92,7 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { p += 4; } *asc_out = '\0'; - _LOG(log, scopeFlags, " %s %s\n", code_buffer, ascii_buffer); + _LOG(log, scope_flags, " %s %s\n", code_buffer, ascii_buffer); } } @@ -100,15 +100,13 @@ static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scopeFlags) { * If configured to do so, dump memory around *all* registers * for the crashing thread. */ -void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { pt_regs_mips_t r; if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { return; } - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { + if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) { static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; for (int reg = 0; reg < 32; reg++) { @@ -130,50 +128,47 @@ void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), continue; } - _LOG(log, scopeFlags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); - dump_memory(log, tid, addr, scopeFlags | SCOPE_SENSITIVE); + _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE); } } unsigned int pc = R(r.cp0_epc); unsigned int ra = R(r.regs[31]); - _LOG(log, scopeFlags, "\ncode around pc:\n"); - dump_memory(log, tid, (uintptr_t)pc, scopeFlags); + _LOG(log, scope_flags, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)pc, scope_flags); if (pc != ra) { - _LOG(log, scopeFlags, "\ncode around ra:\n"); - dump_memory(log, tid, (uintptr_t)ra, scopeFlags); + _LOG(log, scope_flags, "\ncode around ra:\n"); + dump_memory(log, tid, (uintptr_t)ra, scope_flags); } } -void dump_registers(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) +void dump_registers(log_t* log, pid_t tid, int scope_flags) { pt_regs_mips_t r; - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); return; } - _LOG(log, scopeFlags, " zr %08x at %08x v0 %08x v1 %08x\n", + _LOG(log, scope_flags, " zr %08x at %08x v0 %08x v1 %08x\n", R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, scopeFlags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + _LOG(log, scope_flags, " a0 %08x a1 %08x a2 %08x a3 %08x\n", R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, scopeFlags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + _LOG(log, scope_flags, " t0 %08x t1 %08x t2 %08x t3 %08x\n", R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, scopeFlags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + _LOG(log, scope_flags, " t4 %08x t5 %08x t6 %08x t7 %08x\n", R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, scopeFlags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + _LOG(log, scope_flags, " s0 %08x s1 %08x s2 %08x s3 %08x\n", R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, scopeFlags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + _LOG(log, scope_flags, " s4 %08x s5 %08x s6 %08x s7 %08x\n", R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, scopeFlags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + _LOG(log, scope_flags, " t8 %08x t9 %08x k0 %08x k1 %08x\n", R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, scopeFlags, " gp %08x sp %08x s8 %08x ra %08x\n", + _LOG(log, scope_flags, " gp %08x sp %08x s8 %08x ra %08x\n", R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, scopeFlags, " hi %08x lo %08x bva %08x epc %08x\n", + _LOG(log, scope_flags, " hi %08x lo %08x bva %08x epc %08x\n", R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); } diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c index 7009a8e..aa63547 100644 --- a/debuggerd/tombstone.c +++ b/debuggerd/tombstone.c @@ -32,8 +32,7 @@ #include <log/logger.h> #include <cutils/properties.h> -#include <corkscrew/demangle.h> -#include <corkscrew/backtrace.h> +#include <backtrace/backtrace.h> #include <sys/socket.h> #include <linux/un.h> @@ -42,9 +41,8 @@ #include "machine.h" #include "tombstone.h" -#include "utility.h" +#include "backtrace.h" -#define STACK_DEPTH 32 #define STACK_WORDS 16 #define MAX_TOMBSTONES 10 @@ -193,7 +191,7 @@ static void dump_fault_addr(log_t* log, pid_t tid, int sig) } } -static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) { +static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) { char path[64]; char threadnamebuf[1024]; char* threadname = NULL; @@ -211,7 +209,7 @@ static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) { } } - if (at_fault) { + if (IS_AT_FAULT(scope_flags)) { char procnamebuf[1024]; char* procname = NULL; @@ -230,64 +228,46 @@ static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) { } } -static void dump_backtrace(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid __attribute((unused)), bool at_fault, - const backtrace_frame_t* backtrace, size_t frames) { - int scopeFlags = at_fault ? SCOPE_AT_FAULT : 0; - _LOG(log, scopeFlags, "\nbacktrace:\n"); - - backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; - get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); - for (size_t i = 0; i < frames; i++) { - char line[MAX_BACKTRACE_LINE_LENGTH]; - format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], - line, MAX_BACKTRACE_LINE_LENGTH); - _LOG(log, scopeFlags, " %s\n", line); - } - free_backtrace_symbols(backtrace_symbols, frames); -} - -static void dump_stack_segment(const ptrace_context_t* context, log_t* log, pid_t tid, - int scopeFlags, uintptr_t* sp, size_t words, int label) { +static void dump_stack_segment(const backtrace_context_t* context, log_t* log, + int scope_flags, uintptr_t *sp, size_t words, int label) { for (size_t i = 0; i < words; i++) { uint32_t stack_content; - if (!try_get_word_ptrace(tid, *sp, &stack_content)) { + if (!backtrace_read_word(context, *sp, &stack_content)) { break; } - const map_info_t* mi; - const symbol_t* symbol; - find_symbol_ptrace(context, stack_content, &mi, &symbol); - - if (symbol) { - char* demangled_name = demangle_symbol_name(symbol->name); - const char* symbol_name = demangled_name ? demangled_name : symbol->name; - uint32_t offset = stack_content - (mi->start + symbol->start); + const char* map_name = backtrace_get_map_name(context, stack_content, NULL); + if (!map_name) { + map_name = ""; + } + uintptr_t offset = 0; + char* func_name = backtrace_get_func_name(context, stack_content, &offset); + if (func_name) { if (!i && label >= 0) { if (offset) { - _LOG(log, scopeFlags, " #%02d %08x %08x %s (%s+%u)\n", - label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset); + _LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n", + label, *sp, stack_content, map_name, func_name, offset); } else { - _LOG(log, scopeFlags, " #%02d %08x %08x %s (%s)\n", - label, *sp, stack_content, mi ? mi->name : "", symbol_name); + _LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n", + label, *sp, stack_content, map_name, func_name); } } else { if (offset) { - _LOG(log, scopeFlags, " %08x %08x %s (%s+%u)\n", - *sp, stack_content, mi ? mi->name : "", symbol_name, offset); + _LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n", + *sp, stack_content, map_name, func_name, offset); } else { - _LOG(log, scopeFlags, " %08x %08x %s (%s)\n", - *sp, stack_content, mi ? mi->name : "", symbol_name); + _LOG(log, scope_flags, " %08x %08x %s (%s)\n", + *sp, stack_content, map_name, func_name); } } - free(demangled_name); + free(func_name); } else { if (!i && label >= 0) { - _LOG(log, scopeFlags, " #%02d %08x %08x %s\n", - label, *sp, stack_content, mi ? mi->name : ""); + _LOG(log, scope_flags, " #%02d %08x %08x %s\n", + label, *sp, stack_content, map_name); } else { - _LOG(log, scopeFlags, " %08x %08x %s\n", - *sp, stack_content, mi ? mi->name : ""); + _LOG(log, scope_flags, " %08x %08x %s\n", + *sp, stack_content, map_name); } } @@ -295,45 +275,43 @@ static void dump_stack_segment(const ptrace_context_t* context, log_t* log, pid_ } } -static void dump_stack(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, - const backtrace_frame_t* backtrace, size_t frames) { - bool have_first = false; - size_t first, last; - for (size_t i = 0; i < frames; i++) { - if (backtrace[i].stack_top) { - if (!have_first) { - have_first = true; - first = i; +static void dump_stack(const backtrace_context_t* context, log_t* log, int scope_flags) { + const backtrace_t* backtrace = context->backtrace; + size_t first = 0, last; + for (size_t i = 0; i < backtrace->num_frames; i++) { + if (backtrace->frames[i].sp) { + if (!first) { + first = i+1; } last = i; } } - if (!have_first) { + if (!first) { return; } + first--; - int scopeFlags = SCOPE_SENSITIVE | (at_fault ? SCOPE_AT_FAULT : 0); - _LOG(log, scopeFlags, "\nstack:\n"); + scope_flags |= SCOPE_SENSITIVE; // Dump a few words before the first frame. - uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t); - dump_stack_segment(context, log, tid, scopeFlags, &sp, STACK_WORDS, -1); + uintptr_t sp = backtrace->frames[first].sp - STACK_WORDS * sizeof(uint32_t); + dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, -1); // Dump a few words from all successive frames. // Only log the first 3 frames, put the rest in the tombstone. for (size_t i = first; i <= last; i++) { - const backtrace_frame_t* frame = &backtrace[i]; - if (sp != frame->stack_top) { - _LOG(log, scopeFlags, " ........ ........\n"); - sp = frame->stack_top; + const backtrace_frame_data_t* frame = &backtrace->frames[i]; + if (sp != frame->sp) { + _LOG(log, scope_flags, " ........ ........\n"); + sp = frame->sp; } if (i - first == 3) { - scopeFlags &= (~SCOPE_AT_FAULT); + scope_flags &= (~SCOPE_AT_FAULT); } if (i == last) { - dump_stack_segment(context, log, tid, scopeFlags, &sp, STACK_WORDS, i); - if (sp < frame->stack_top + frame->stack_size) { - _LOG(log, scopeFlags, " ........ ........\n"); + dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, i); + if (sp < frame->sp + frame->stack_size) { + _LOG(log, scope_flags, " ........ ........\n"); } } else { size_t words = frame->stack_size / sizeof(uint32_t); @@ -342,39 +320,40 @@ static void dump_stack(const ptrace_context_t* context, log_t* log, pid_t tid, b } else if (words > STACK_WORDS) { words = STACK_WORDS; } - dump_stack_segment(context, log, tid, scopeFlags, &sp, words, i); + dump_stack_segment(context, log, scope_flags, &sp, words, i); } } } -static void dump_backtrace_and_stack(const ptrace_context_t* context, log_t* log, pid_t tid, - bool at_fault) { - backtrace_frame_t backtrace[STACK_DEPTH]; - ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); - if (frames > 0) { - dump_backtrace(context, log, tid, at_fault, backtrace, frames); - dump_stack(context, log, tid, at_fault, backtrace, frames); +static void dump_backtrace_and_stack(const backtrace_context_t* context, + log_t* log, int scope_flags) { + if (context->backtrace->num_frames) { + _LOG(log, scope_flags, "\nbacktrace:\n"); + dump_backtrace_to_log(context, log, scope_flags, " "); + + _LOG(log, scope_flags, "\nstack:\n"); + dump_stack(context, log, scope_flags); } } -static void dump_map(log_t* log, map_info_t* m, const char* what, int scopeFlags) { +static void dump_map(log_t* log, const backtrace_map_info_t* m, const char* what, int scope_flags) { if (m != NULL) { - _LOG(log, scopeFlags, " %08x-%08x %c%c%c %s\n", m->start, m->end, + _LOG(log, scope_flags, " %08x-%08x %c%c%c %s\n", m->start, m->end, m->is_readable ? 'r' : '-', m->is_writable ? 'w' : '-', m->is_executable ? 'x' : '-', m->name); } else { - _LOG(log, scopeFlags, " (no %s)\n", what); + _LOG(log, scope_flags, " (no %s)\n", what); } } -static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault) { - int scopeFlags = SCOPE_SENSITIVE | (at_fault ? SCOPE_AT_FAULT : 0); +static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* log, pid_t tid, int scope_flags) { + scope_flags |= SCOPE_SENSITIVE; siginfo_t si; memset(&si, 0, sizeof(si)); if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { - _LOG(log, scopeFlags, "cannot get siginfo for %d: %s\n", + _LOG(log, scope_flags, "cannot get siginfo for %d: %s\n", tid, strerror(errno)); return; } @@ -388,15 +367,15 @@ static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t return; } - _LOG(log, scopeFlags, "\nmemory map around fault addr %08x:\n", (int)si.si_addr); + _LOG(log, scope_flags, "\nmemory map around fault addr %08x:\n", (int)si.si_addr); /* * Search for a match, or for a hole where the match would be. The list * is backward from the file content, so it starts at high addresses. */ - map_info_t* map = context->map_info_list; - map_info_t *next = NULL; - map_info_t *prev = NULL; + const backtrace_map_info_t* map = map_info_list; + const backtrace_map_info_t* next = NULL; + const backtrace_map_info_t* prev = NULL; while (map != NULL) { if (addr >= map->start && addr < map->end) { next = map->next; @@ -416,31 +395,32 @@ static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t * Show "next" then "match" then "prev" so that the addresses appear in * ascending order (like /proc/pid/maps). */ - dump_map(log, next, "map below", scopeFlags); - dump_map(log, map, "map for address", scopeFlags); - dump_map(log, prev, "map above", scopeFlags); + dump_map(log, next, "map below", scope_flags); + dump_map(log, map, "map for address", scope_flags); + dump_map(log, prev, "map above", scope_flags); } -static void dump_thread(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, - int* total_sleep_time_usec) { - wait_for_stop(tid, total_sleep_time_usec); +static void dump_thread(const backtrace_context_t* context, log_t* log, + int scope_flags, int* total_sleep_time_usec) { + const backtrace_t* backtrace = context->backtrace; + wait_for_stop(backtrace->tid, total_sleep_time_usec); - dump_registers(context, log, tid, at_fault); - dump_backtrace_and_stack(context, log, tid, at_fault); - if (at_fault) { - dump_memory_and_code(context, log, tid, at_fault); - dump_nearby_maps(context, log, tid, at_fault); + dump_registers(log, backtrace->tid, scope_flags); + dump_backtrace_and_stack(context, log, scope_flags); + if (IS_AT_FAULT(scope_flags)) { + dump_memory_and_code(log, backtrace->tid, scope_flags); + dump_nearby_maps(backtrace->map_info_list, log, backtrace->tid, scope_flags); } } /* Return true if some thread is not detached cleanly */ -static bool dump_sibling_thread_report(const ptrace_context_t* context, +static bool dump_sibling_thread_report( log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec) { char task_path[64]; snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); DIR* d = opendir(task_path); - /* Bail early if cannot open the task directory */ + /* Bail early if the task directory cannot be opened */ if (d == NULL) { XLOG("Cannot open /proc/%d/task\n", pid); return false; @@ -467,8 +447,12 @@ static bool dump_sibling_thread_report(const ptrace_context_t* context, } _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); - dump_thread_info(log, pid, new_tid, false); - dump_thread(context, log, new_tid, false, total_sleep_time_usec); + dump_thread_info(log, pid, new_tid, 0); + backtrace_context_t new_context; + if (backtrace_create_context(&new_context, pid, new_tid, 0)) { + dump_thread(&new_context, log, 0, total_sleep_time_usec); + backtrace_destroy_context(&new_context); + } if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); @@ -624,7 +608,7 @@ static void dump_logs(log_t* log, pid_t pid, bool tailOnly) dump_log_file(log, pid, "/dev/log/main", tailOnly); } -static void dump_abort_message(log_t* log, pid_t tid, uintptr_t address) { +static void dump_abort_message(const backtrace_context_t* context, log_t* log, uintptr_t address) { if (address == 0) { return; } @@ -636,7 +620,7 @@ static void dump_abort_message(log_t* log, pid_t tid, uintptr_t address) { char* p = &msg[0]; while (p < &msg[sizeof(msg)]) { uint32_t data; - if (!try_get_word_ptrace(tid, address, &data)) { + if (!backtrace_read_word(context, address, &data)) { break; } address += sizeof(uint32_t); @@ -686,14 +670,17 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); dump_build_info(log); dump_revision_info(log); - dump_thread_info(log, pid, tid, true); + dump_thread_info(log, pid, tid, SCOPE_AT_FAULT); if (signal) { dump_fault_addr(log, tid, signal); } - dump_abort_message(log, tid, abort_msg_address); - ptrace_context_t* context = load_ptrace_context(tid); - dump_thread(context, log, tid, true, total_sleep_time_usec); + backtrace_context_t context; + if (backtrace_create_context(&context, pid, tid, 0)) { + dump_abort_message(&context, log, abort_msg_address); + dump_thread(&context, log, SCOPE_AT_FAULT, total_sleep_time_usec); + backtrace_destroy_context(&context); + } if (want_logs) { dump_logs(log, pid, true); @@ -701,11 +688,9 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a bool detach_failed = false; if (dump_sibling_threads) { - detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec); + detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec); } - free_ptrace_context(context); - if (want_logs) { dump_logs(log, pid, false); } diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c index af79092..e00208d 100644 --- a/debuggerd/x86/machine.c +++ b/debuggerd/x86/machine.c @@ -31,28 +31,24 @@ #include "../utility.h" #include "../machine.h" -void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { +void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) { } -void dump_registers(const ptrace_context_t* context __attribute((unused)), - log_t* log, pid_t tid, bool at_fault) { +void dump_registers(log_t* log, pid_t tid, int scope_flags) { struct pt_regs_x86 r; - int scopeFlags = (at_fault ? SCOPE_AT_FAULT : 0); - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - _LOG(log, scopeFlags, "cannot get registers: %s\n", strerror(errno)); + _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno)); return; } //if there is no stack, no print just like arm if(!r.ebp) return; - _LOG(log, scopeFlags, " eax %08x ebx %08x ecx %08x edx %08x\n", + _LOG(log, scope_flags, " eax %08x ebx %08x ecx %08x edx %08x\n", r.eax, r.ebx, r.ecx, r.edx); - _LOG(log, scopeFlags, " esi %08x edi %08x\n", + _LOG(log, scope_flags, " esi %08x edi %08x\n", r.esi, r.edi); - _LOG(log, scopeFlags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", + _LOG(log, scope_flags, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", r.xcs, r.xds, r.xes, r.xfs, r.xss); - _LOG(log, scopeFlags, " eip %08x ebp %08x esp %08x flags %08x\n", + _LOG(log, scope_flags, " eip %08x ebp %08x esp %08x flags %08x\n", r.eip, r.ebp, r.esp, r.eflags); } diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 1189e1f..b9b3c92 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -18,9 +18,10 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ $(LOCAL_PATH)/../../extras/ext4_utils -LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c +LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c LOCAL_MODULE := fastboot LOCAL_MODULE_TAGS := debug +LOCAL_CFLAGS += -std=gnu99 ifeq ($(HOST_OS),linux) LOCAL_SRC_FILES += usb_linux.c util_linux.c @@ -69,7 +70,7 @@ $(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) -LOCAL_SRC_FILES := usbtest.c usb_linux.c +LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c LOCAL_MODULE := usbtest include $(BUILD_HOST_EXECUTABLE) endif diff --git a/fastboot/engine.c b/fastboot/engine.c index b07e742..972c4ed 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -36,7 +36,6 @@ #include <stdbool.h> #include <string.h> #include <sys/stat.h> -#include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -48,34 +47,13 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) -double now() -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; -} - -char *mkmsg(const char *fmt, ...) -{ - char buf[256]; - char *s; - va_list ap; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - - s = strdup(buf); - if (s == 0) die("out of memory"); - return s; -} - #define OP_DOWNLOAD 1 #define OP_COMMAND 2 #define OP_QUERY 3 #define OP_NOTICE 4 #define OP_FORMAT 5 #define OP_DOWNLOAD_SPARSE 6 +#define OP_WAIT_FOR_DISCONNECT 7 typedef struct Action Action; @@ -573,6 +551,11 @@ void fb_queue_notice(const char *notice) a->data = (void*) notice; } +void fb_queue_wait_for_disconnect(void) +{ + queue_action(OP_WAIT_FOR_DISCONNECT, ""); +} + int fb_execute_queue(usb_handle *usb) { Action *a; @@ -614,6 +597,8 @@ int fb_execute_queue(usb_handle *usb) status = fb_download_data_sparse(usb, a->data); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; + } else if (a->op == OP_WAIT_FOR_DISCONNECT) { + usb_wait_for_disconnect(usb); } else { die("bogus action"); } diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index f186c93..73a6e56 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -30,7 +30,6 @@ #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> #include <stdbool.h> #include <stdint.h> #include <string.h> @@ -106,17 +105,6 @@ static struct { {"system.img", "system.sig", "system", false}, }; -void die(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr,"error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr,"\n"); - va_end(ap); - exit(1); -} - void get_my_path(char *path); char *find_item(const char *item, const char *product) @@ -503,7 +491,13 @@ static int setup_requirement_line(char *name) for(n = 0; n < count; n++) { out[n] = strdup(strip(val[n])); - if (out[n] == 0) return -1; + if (out[n] == 0) { + for(size_t i = 0; i < n; ++i) { + free((char*) out[i]); + } + free(out); + return -1; + } } fb_queue_require(prod, name, invert, n, out); @@ -1102,6 +1096,7 @@ int main(int argc, char **argv) fb_queue_reboot(); } else if (wants_reboot_bootloader) { fb_queue_command("reboot-bootloader", "rebooting into bootloader"); + fb_queue_wait_for_disconnect(); } if (fb_queue_is_empty()) diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index c1b2964..976c397 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -58,10 +58,13 @@ void fb_queue_reboot(void); void fb_queue_command(const char *cmd, const char *msg); void fb_queue_download(const char *name, void *data, unsigned size); void fb_queue_notice(const char *notice); +void fb_queue_wait_for_disconnect(void); int fb_execute_queue(usb_handle *usb); int fb_queue_is_empty(void); /* util stuff */ +double now(); +char *mkmsg(const char *fmt, ...); void die(const char *fmt, ...); /* Current product */ diff --git a/fastboot/usb.h b/fastboot/usb.h index d504ee2..17cf0a9 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -62,6 +62,6 @@ usb_handle *usb_open(ifc_match_func callback); int usb_close(usb_handle *h); int usb_read(usb_handle *h, void *_data, int len); int usb_write(usb_handle *h, const void *_data, int len); - +int usb_wait_for_disconnect(usb_handle *h); #endif diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index b7a9ca3..f2ce226 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -50,10 +50,17 @@ #endif #include <asm/byteorder.h> +#include "fastboot.h" #include "usb.h" #define MAX_RETRIES 5 +/* Timeout in seconds for usb_wait_for_disconnect. + * It doesn't usually take long for a device to disconnect (almost always + * under 2 seconds) but we'll time out after 3 seconds just in case. + */ +#define WAIT_FOR_DISCONNECT_TIMEOUT 3 + #ifdef TRACE_USB #define DBG1(x...) fprintf(stderr, x) #define DBG(x...) fprintf(stderr, x) @@ -75,10 +82,18 @@ struct usb_handle unsigned char ep_out; }; +/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices. + * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'. + * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1'). + * The name must also start with a digit, to disallow '.' and '..' + */ static inline int badname(const char *name) { - while(*name) { - if(!isdigit(*name++)) return 1; + if (!isdigit(*name)) + return 1; + while(*++name) { + if(!isdigit(*name) && *name != '.' && *name != '-') + return 1; } return 0; } @@ -95,7 +110,8 @@ static int check(void *_desc, int len, unsigned type, int size) return 0; } -static int filter_usb_device(int fd, char *ptr, int len, int writable, +static int filter_usb_device(char* sysfs_name, + char *ptr, int len, int writable, ifc_match_func callback, int *ept_in_id, int *ept_out_id, int *ifc_id) { @@ -131,69 +147,35 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, info.dev_protocol = dev->bDeviceProtocol; info.writable = writable; - // read device serial number (if there is one) - info.serial_number[0] = 0; - if (dev->iSerialNumber) { - struct usbdevfs_ctrltransfer ctrl; - // Keep it short enough because some bootloaders are borked if the URB len is > 255 - // 128 is too big by 1. - __u16 buffer[127]; - - memset(buffer, 0, sizeof(buffer)); - - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber; - //language ID (en-us) for serial number string - ctrl.wIndex = 0x0409; - ctrl.wLength = sizeof(buffer); - ctrl.data = buffer; - ctrl.timeout = 50; - - result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); - if (result > 0) { - int i; - // skip first word, and copy the rest to the serial string, changing shorts to bytes. - result /= 2; - for (i = 1; i < result; i++) - info.serial_number[i - 1] = buffer[i]; - info.serial_number[i - 1] = 0; - } - } + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name); - /* We need to get a path that represents a particular port on a particular - * hub. We are passed an fd that was obtained by opening an entry under - * /dev/bus/usb. Unfortunately, the names of those entries change each - * time devices are plugged and unplugged. So how to get a repeatable - * path? udevadm provided the inspiration. We can get the major and - * minor of the device file, read the symlink that can be found here: - * /sys/dev/char/<major>:<minor> - * and then use the last element of that path. As a concrete example, I - * have an Android device at /dev/bus/usb/001/027 so working with bash: - * $ ls -l /dev/bus/usb/001/027 - * crw-rw-r-- 1 root plugdev 189, 26 Apr 9 11:03 /dev/bus/usb/001/027 - * $ ls -l /sys/dev/char/189:26 - * lrwxrwxrwx 1 root root 0 Apr 9 11:03 /sys/dev/char/189:26 -> - * ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3 - * So our device_path would be 1-4.2.3 which says my device is connected - * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per - * http://www.linux-usb.org/FAQ.html). + /* Read device serial number (if there is one). + * We read the serial number from sysfs, since it's faster and more + * reliable than issuing a control pipe read, and also won't + * cause problems for devices which don't like getting descriptor + * requests while they're in the middle of flashing. */ - info.device_path[0] = '\0'; - result = fstat(fd, &st); - if (!result && S_ISCHR(st.st_mode)) { - char cdev[128]; - char link[256]; - char *slash; - ssize_t link_len; - snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d", - major(st.st_rdev), minor(st.st_rdev)); - link_len = readlink(cdev, link, sizeof(link) - 1); - if (link_len > 0) { - link[link_len] = '\0'; - slash = strrchr(link, '/'); - if (slash) - snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1); + info.serial_number[0] = '\0'; + if (dev->iSerialNumber) { + char path[80]; + int fd; + + snprintf(path, sizeof(path), + "/sys/bus/usb/devices/%s/serial", sysfs_name); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_RDONLY); + if (fd >= 0) { + int chars_read = read(fd, info.serial_number, + sizeof(info.serial_number) - 1); + close(fd); + + if (chars_read <= 0) + info.serial_number[0] = '\0'; + else if (info.serial_number[chars_read - 1] == '\n') { + // strip trailing newline + info.serial_number[chars_read - 1] = '\0'; + } } } @@ -241,14 +223,73 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, return -1; } +static int read_sysfs_string(const char *sysfs_name, const char *sysfs_node, + char* buf, int bufsize) +{ + char path[80]; + int fd, n; + + snprintf(path, sizeof(path), + "/sys/bus/usb/devices/%s/%s", sysfs_name, sysfs_node); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + n = read(fd, buf, bufsize - 1); + close(fd); + + if (n < 0) + return -1; + + buf[n] = '\0'; + + return n; +} + +static int read_sysfs_number(const char *sysfs_name, const char *sysfs_node) +{ + char buf[16]; + int value; + + if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0) + return -1; + + if (sscanf(buf, "%d", &value) != 1) + return -1; + + return value; +} + +/* Given the name of a USB device in sysfs, get the name for the same + * device in devfs. Returns 0 for success, -1 for failure. + */ +static int convert_to_devfs_name(const char* sysfs_name, + char* devname, int devname_size) +{ + int busnum, devnum; + + busnum = read_sysfs_number(sysfs_name, "busnum"); + if (busnum < 0) + return -1; + + devnum = read_sysfs_number(sysfs_name, "devnum"); + if (devnum < 0) + return -1; + + snprintf(devname, devname_size, "/dev/bus/usb/%03d/%03d", busnum, devnum); + return 0; +} + static usb_handle *find_usb_device(const char *base, ifc_match_func callback) { usb_handle *usb = 0; - char busname[64], devname[64]; + char devname[64]; char desc[1024]; int n, in, out, ifc; - DIR *busdir, *devdir; + DIR *busdir; struct dirent *de; int fd; int writable; @@ -259,15 +300,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) while((de = readdir(busdir)) && (usb == 0)) { if(badname(de->d_name)) continue; - sprintf(busname, "%s/%s", base, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - -// DBG("[ scanning %s ]\n", busname); - while((de = readdir(devdir)) && (usb == 0)) { - - if(badname(de->d_name)) continue; - sprintf(devname, "%s/%s", busname, de->d_name); + if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) { // DBG("[ scanning %s ]\n", devname); writable = 1; @@ -282,7 +315,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) n = read(fd, desc, sizeof(desc)); - if(filter_usb_device(fd, desc, n, writable, callback, + if(filter_usb_device(de->d_name, desc, n, writable, callback, &in, &out, &ifc) == 0) { usb = calloc(1, sizeof(usb_handle)); strcpy(usb->fname, devname); @@ -301,7 +334,6 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) close(fd); } } - closedir(devdir); } closedir(busdir); @@ -315,26 +347,11 @@ int usb_write(usb_handle *h, const void *_data, int len) struct usbdevfs_bulktransfer bulk; int n; - if(h->ep_out == 0) { + if(h->ep_out == 0 || h->desc == -1) { return -1; } - if(len == 0) { - bulk.ep = h->ep_out; - bulk.len = 0; - bulk.data = data; - bulk.timeout = 0; - - n = ioctl(h->desc, USBDEVFS_BULK, &bulk); - if(n != 0) { - fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); - return -1; - } - return 0; - } - - while(len > 0) { + do { int xfer; xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; @@ -353,7 +370,7 @@ int usb_write(usb_handle *h, const void *_data, int len) count += xfer; len -= xfer; data += xfer; - } + } while(len > 0); return count; } @@ -365,7 +382,7 @@ int usb_read(usb_handle *h, void *_data, int len) struct usbdevfs_bulktransfer bulk; int n, retry; - if(h->ep_in == 0) { + if(h->ep_in == 0 || h->desc == -1) { return -1; } @@ -431,5 +448,20 @@ int usb_close(usb_handle *h) usb_handle *usb_open(ifc_match_func callback) { - return find_usb_device("/dev/bus/usb", callback); + return find_usb_device("/sys/bus/usb/devices", callback); +} + +/* Wait for the system to notice the device is gone, so that a subsequent + * fastboot command won't try to access the device before it's rebooted. + * Returns 0 for success, -1 for timeout. + */ +int usb_wait_for_disconnect(usb_handle *usb) +{ + double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT; + while (now() < deadline) { + if (access(usb->fname, F_OK)) + return 0; + usleep(50000); + } + return -1; } diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index 1548ba8..0f55e0d 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -467,6 +467,11 @@ int usb_close(usb_handle *h) { return 0; } +int usb_wait_for_disconnect(usb_handle *usb) { + /* TODO: Punt for now */ + return 0; +} + int usb_read(usb_handle *h, void *data, int len) { IOReturn result; UInt32 numBytes = len; diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 7aa36b2..07f7be2 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -269,6 +269,11 @@ int usb_close(usb_handle* handle) { return 0; } +int usb_wait_for_disconnect(usb_handle *usb) { + /* TODO: Punt for now */ + return 0; +} + int recognized_device(usb_handle* handle, ifc_match_func callback) { struct usb_ifc_info info; USB_DEVICE_DESCRIPTOR device_desc; diff --git a/fastboot/util.c b/fastboot/util.c new file mode 100644 index 0000000..f2bbd34 --- /dev/null +++ b/fastboot/util.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/time.h> + +#include "fastboot.h" + +double now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; +} + +char *mkmsg(const char *fmt, ...) +{ + char buf[256]; + char *s; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + s = strdup(buf); + if (s == 0) die("out of memory"); + return s; +} + +void die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr,"error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index f432f6a..13b71ee 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -238,74 +238,16 @@ out: return f; } -/* Read a line of text till the next newline character. - * If no newline is found before the buffer is full, continue reading till a new line is seen, - * then return an empty buffer. This effectively ignores lines that are too long. - * On EOF, return null. - */ -static char *fs_getline(char *buf, int size, FILE *file) -{ - int cnt = 0; - int eof = 0; - int eol = 0; - int c; - - if (size < 1) { - return NULL; - } - - while (cnt < (size - 1)) { - c = getc(file); - if (c == EOF) { - eof = 1; - break; - } - - *(buf + cnt) = c; - cnt++; - - if (c == '\n') { - eol = 1; - break; - } - } - - /* Null terminate what we've read */ - *(buf + cnt) = '\0'; - - if (eof) { - if (cnt) { - return buf; - } else { - return NULL; - } - } else if (eol) { - return buf; - } else { - /* The line is too long. Read till a newline or EOF. - * If EOF, return null, if newline, return an empty buffer. - */ - while(1) { - c = getc(file); - if (c == EOF) { - return NULL; - } else if (c == '\n') { - *buf = '\0'; - return buf; - } - } - } -} - struct fstab *fs_mgr_read_fstab(const char *fstab_path) { FILE *fstab_file; int cnt, entries; - int len; - char line[256]; + ssize_t len; + size_t alloc_len = 0; + char *line = NULL; const char *delim = " \t"; char *save_ptr, *p; - struct fstab *fstab; + struct fstab *fstab = NULL; struct fstab_rec *recs; struct fs_mgr_flag_values flag_vals; #define FS_OPTIONS_LEN 1024 @@ -318,9 +260,8 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) } entries = 0; - while (fs_getline(line, sizeof(line), fstab_file)) { + while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { /* if the last character is a newline, shorten the string by 1 byte */ - len = strlen(line); if (line[len - 1] == '\n') { line[len - 1] = '\0'; } @@ -337,7 +278,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) if (!entries) { ERROR("No entries found in fstab\n"); - return 0; + goto err; } /* Allocate and init the fstab structure */ @@ -349,9 +290,8 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) fseek(fstab_file, 0, SEEK_SET); cnt = 0; - while (fs_getline(line, sizeof(line), fstab_file)) { + while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { /* if the last character is a newline, shorten the string by 1 byte */ - len = strlen(line); if (line[len - 1] == '\n') { line[len - 1] = '\0'; } @@ -376,25 +316,25 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) if (!(p = strtok_r(line, delim, &save_ptr))) { ERROR("Error parsing mount source\n"); - return 0; + goto err; } fstab->recs[cnt].blk_device = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing mount_point\n"); - return 0; + goto err; } fstab->recs[cnt].mount_point = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing fs_type\n"); - return 0; + goto err; } fstab->recs[cnt].fs_type = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing mount_flags\n"); - return 0; + goto err; } tmp_fs_options[0] = '\0'; fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, @@ -409,7 +349,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing fs_mgr_options\n"); - return 0; + goto err; } fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, &flag_vals, NULL, 0); @@ -422,8 +362,15 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) cnt++; } fclose(fstab_file); - + free(line); return fstab; + +err: + fclose(fstab_file); + free(line); + if (fstab) + fs_mgr_free_fstab(fstab); + return NULL; } void fs_mgr_free_fstab(struct fstab *fstab) @@ -442,7 +389,6 @@ void fs_mgr_free_fstab(struct fstab *fstab) free(fstab->recs[i].fs_options); free(fstab->recs[i].key_loc); free(fstab->recs[i].label); - i++; } /* Free the fstab_recs array created by calloc(3) */ diff --git a/gpttool/gpttool.c b/gpttool/gpttool.c index 05d5177..d3f08fe 100644 --- a/gpttool/gpttool.c +++ b/gpttool/gpttool.c @@ -161,7 +161,7 @@ void show(struct ptable *ptbl) { struct efi_entry *entry = ptbl->entry; unsigned n, m; - char name[EFI_NAMELEN]; + char name[EFI_NAMELEN + 1]; fprintf(stderr,"ptn start block end block name\n"); fprintf(stderr,"---- ------------- ------------- --------------------\n"); diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h new file mode 100644 index 0000000..b15678c --- /dev/null +++ b/include/backtrace/Backtrace.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _BACKTRACE_BACKTRACE_H +#define _BACKTRACE_BACKTRACE_H + +#include <backtrace/backtrace.h> + +#include <string> + +class BacktraceImpl; + +class Backtrace { +public: + // Create the correct Backtrace object based on what is to be unwound. + // If pid < 0 or equals the current pid, then the Backtrace object + // corresponds to the current process. + // If pid < 0 or equals the current pid and tid >= 0, then the Backtrace + // object corresponds to a thread in the current process. + // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a + // different process. + // Tracing a thread in a different process is not supported. + static Backtrace* Create(pid_t pid, pid_t tid); + + virtual ~Backtrace(); + + // Get the current stack trace and store in the backtrace_ structure. + virtual bool Unwind(size_t num_ignore_frames); + + // Get the function name and offset into the function given the pc. + // If the string is empty, then no valid function name was found. + virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset); + + // Get the name of the map associated with the given pc. If NULL is returned, + // then map_start is not set. Otherwise, map_start is the beginning of this + // map. + virtual const char* GetMapName(uintptr_t pc, uintptr_t* map_start); + + // Finds the memory map associated with the given ptr. + virtual const backtrace_map_info_t* FindMapInfo(uintptr_t ptr); + + // Read the data at a specific address. + virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0; + + // Create a string representing the formatted line of backtrace information + // for a single frame. + virtual std::string FormatFrameData(size_t frame_num); + + pid_t Pid() { return backtrace_.pid; } + pid_t Tid() { return backtrace_.tid; } + size_t NumFrames() { return backtrace_.num_frames; } + + const backtrace_t* GetBacktrace() { return &backtrace_; } + + const backtrace_frame_data_t* GetFrame(size_t frame_num) { + return &backtrace_.frames[frame_num]; + } + +protected: + Backtrace(BacktraceImpl* impl); + + virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value); + + BacktraceImpl* impl_; + + backtrace_map_info_t* map_info_; + + backtrace_t backtrace_; + + friend class BacktraceImpl; +}; + +#endif // _BACKTRACE_BACKTRACE_H diff --git a/include/backtrace/backtrace.h b/include/backtrace/backtrace.h new file mode 100644 index 0000000..b35a6d5 --- /dev/null +++ b/include/backtrace/backtrace.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _BACKTRACE_H +#define _BACKTRACE_H + +#include <sys/types.h> +#include <stdbool.h> +#include <inttypes.h> + +__BEGIN_DECLS + +#define MAX_BACKTRACE_FRAMES 64 + +typedef struct backtrace_map_info { + struct backtrace_map_info* next; + uintptr_t start; + uintptr_t end; + bool is_readable; + bool is_writable; + bool is_executable; + char name[]; +} backtrace_map_info_t; + +typedef struct { + uintptr_t pc; /* The absolute pc. */ + uintptr_t sp; /* The top of the stack. */ + size_t stack_size; /* The size of the stack, zero indicate an unknown stack size. */ + const char* map_name; /* The name of the map to which this pc belongs, NULL indicates the pc doesn't belong to a known map. */ + uintptr_t map_offset; /* pc relative to the start of the map, only valid if map_name is not NULL. */ + char* func_name; /* The function name associated with this pc, NULL if not found. */ + uintptr_t func_offset; /* pc relative to the start of the function, only valid if func_name is not NULL. */ +} backtrace_frame_data_t; + +typedef struct { + backtrace_frame_data_t frames[MAX_BACKTRACE_FRAMES]; + size_t num_frames; + + pid_t pid; + pid_t tid; + backtrace_map_info_t* map_info_list; +} backtrace_t; + +typedef struct { + void* data; + const backtrace_t* backtrace; +} backtrace_context_t; + +/* Create a context for the backtrace data and gather the backtrace. + * If pid < 0, then gather the backtrace for the current process. + */ +bool backtrace_create_context( + backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames); + +/* Gather the backtrace data for a pthread instead of a process. */ +bool backtrace_create_thread_context( + backtrace_context_t* context, pid_t tid, size_t num_ignore_frames); + +/* Free any memory allocated during the context create. */ +void backtrace_destroy_context(backtrace_context_t* context); + +/* Read data at a specific address for a process. */ +bool backtrace_read_word( + const backtrace_context_t* context, uintptr_t ptr, uint32_t* value); + +/* Get information about the map name associated with a pc. If NULL is + * returned, then map_start is not set. + */ +const char* backtrace_get_map_name( + const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start); + +/* Get the function name and offset given the pc. If NULL is returned, + * then func_offset is not set. The returned string is allocated using + * malloc and must be freed by the caller. + */ +char* backtrace_get_func_name( + const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset); + +/* Loads memory map from /proc/<pid>/maps. If pid < 0, then load the memory + * map for the current process. + */ +backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid); + +/* Frees memory associated with the map list. */ +void backtrace_destroy_map_info_list(backtrace_map_info_t* map_info_list); + +/* Finds the memory map that contains the specified pc. */ +const backtrace_map_info_t* backtrace_find_map_info( + const backtrace_map_info_t* map_info_list, uintptr_t pc); + +/* Create a formatted line of backtrace information for a single frame. */ +void backtrace_format_frame_data( + const backtrace_context_t* context, size_t frame_num, char* buf, + size_t buf_size); + +/* Get the backtrace data structure associated with the context. */ +const backtrace_t* backtrace_get_data(backtrace_context_t* context); + +__END_DECLS + +#endif /* _BACKTRACE_H */ diff --git a/include/cutils/list.h b/include/cutils/list.h index 3881fc9..72395f4 100644 --- a/include/cutils/list.h +++ b/include/cutils/list.h @@ -44,6 +44,11 @@ struct listnode #define list_for_each_reverse(node, list) \ for (node = (list)->prev; node != (list); node = node->prev) +#define list_for_each_safe(node, next, list) \ + for (node = (list)->next, next = node->next; \ + node != (list); \ + node = next, next = node->next) + void list_init(struct listnode *list); void list_add_tail(struct listnode *list, struct listnode *item); void list_remove(struct listnode *item); diff --git a/include/mincrypt/dsa_sig.h b/include/mincrypt/dsa_sig.h new file mode 100644 index 0000000..b0d91cd --- /dev/null +++ b/include/mincrypt/dsa_sig.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ + +#include "mincrypt/p256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns 0 if input sig is not a valid ASN.1 sequence +int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int); + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ */ diff --git a/include/mincrypt/hash-internal.h b/include/mincrypt/hash-internal.h index 96806f7..c813b44 100644 --- a/include/mincrypt/hash-internal.h +++ b/include/mincrypt/hash-internal.h @@ -1,8 +1,31 @@ -// Copyright 2007 Google Inc. All Rights Reserved. -// Author: mschilder@google.com (Marius Schilder) - -#ifndef SECURITY_UTIL_LITE_HASH_INTERNAL_H__ -#define SECURITY_UTIL_LITE_HASH_INTERNAL_H__ +/* + * Copyright 2007 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ #include <stdint.h> @@ -37,4 +60,4 @@ typedef struct HASH_CTX { } #endif // __cplusplus -#endif // SECURITY_UTIL_LITE_HASH_INTERNAL_H__ +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ diff --git a/include/mincrypt/p256.h b/include/mincrypt/p256.h new file mode 100644 index 0000000..465a1b9 --- /dev/null +++ b/include/mincrypt/p256.h @@ -0,0 +1,162 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_ + +// Collection of routines manipulating 256 bit unsigned integers. +// Just enough to implement ecdsa-p256 and related algorithms. + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define P256_BITSPERDIGIT 32 +#define P256_NDIGITS 8 +#define P256_NBYTES 32 + +typedef int p256_err; +typedef uint32_t p256_digit; +typedef int32_t p256_sdigit; +typedef uint64_t p256_ddigit; +typedef int64_t p256_sddigit; + +// Defining p256_int as struct to leverage struct assigment. +typedef struct { + p256_digit a[P256_NDIGITS]; +} p256_int; + +extern const p256_int SECP256r1_n; // Curve order +extern const p256_int SECP256r1_p; // Curve prime +extern const p256_int SECP256r1_b; // Curve param + +// Initialize a p256_int to zero. +void p256_init(p256_int* a); + +// Clear a p256_int to zero. +void p256_clear(p256_int* a); + +// Return bit. Index 0 is least significant. +int p256_get_bit(const p256_int* a, int index); + +// b := a % MOD +void p256_mod( + const p256_int* MOD, + const p256_int* a, + p256_int* b); + +// c := a * (top_b | b) % MOD +void p256_modmul( + const p256_int* MOD, + const p256_int* a, + const p256_digit top_b, + const p256_int* b, + p256_int* c); + +// b := 1 / a % MOD +// MOD best be SECP256r1_n +void p256_modinv( + const p256_int* MOD, + const p256_int* a, + p256_int* b); + +// b := 1 / a % MOD +// MOD best be SECP256r1_n +// Faster than p256_modinv() +void p256_modinv_vartime( + const p256_int* MOD, + const p256_int* a, + p256_int* b); + +// b := a << (n % P256_BITSPERDIGIT) +// Returns the bits shifted out of most significant digit. +p256_digit p256_shl(const p256_int* a, int n, p256_int* b); + +// b := a >> (n % P256_BITSPERDIGIT) +void p256_shr(const p256_int* a, int n, p256_int* b); + +int p256_is_zero(const p256_int* a); +int p256_is_odd(const p256_int* a); +int p256_is_even(const p256_int* a); + +// Returns -1, 0 or 1. +int p256_cmp(const p256_int* a, const p256_int *b); + +// c: = a - b +// Returns -1 on borrow. +int p256_sub(const p256_int* a, const p256_int* b, p256_int* c); + +// c := a + b +// Returns 1 on carry. +int p256_add(const p256_int* a, const p256_int* b, p256_int* c); + +// c := a + (single digit)b +// Returns carry 1 on carry. +int p256_add_d(const p256_int* a, p256_digit b, p256_int* c); + +// ec routines. + +// {out_x,out_y} := nG +void p256_base_point_mul(const p256_int *n, + p256_int *out_x, + p256_int *out_y); + +// {out_x,out_y} := n{in_x,in_y} +void p256_point_mul(const p256_int *n, + const p256_int *in_x, + const p256_int *in_y, + p256_int *out_x, + p256_int *out_y); + +// {out_x,out_y} := n1G + n2{in_x,in_y} +void p256_points_mul_vartime( + const p256_int *n1, const p256_int *n2, + const p256_int *in_x, const p256_int *in_y, + p256_int *out_x, p256_int *out_y); + +// Return whether point {x,y} is on curve. +int p256_is_valid_point(const p256_int* x, const p256_int* y); + +// Outputs big-endian binary form. No leading zero skips. +void p256_to_bin(const p256_int* src, uint8_t dst[P256_NBYTES]); + +// Reads from big-endian binary form, +// thus pre-pad with leading zeros if short. +void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst); + +#define P256_DIGITS(x) ((x)->a) +#define P256_DIGIT(x,y) ((x)->a[y]) + +#define P256_ZERO {{0}} +#define P256_ONE {{1}} + +#ifdef __cplusplus +} +#endif + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_ diff --git a/include/mincrypt/p256_ecdsa.h b/include/mincrypt/p256_ecdsa.h new file mode 100644 index 0000000..da339fa --- /dev/null +++ b/include/mincrypt/p256_ecdsa.h @@ -0,0 +1,53 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_ + +// Using current directory as relative include path here since +// this code typically gets lifted into a variety of build systems +// and directory structures. +#include "p256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns 0 if {r,s} is not a signature on message for +// public key {key_x,key_y}. +// +// Note: message is a p256_int. +// Convert from a binary string using p256_from_bin(). +int p256_ecdsa_verify(const p256_int* key_x, + const p256_int* key_y, + const p256_int* message, + const p256_int* r, const p256_int* s); + +#ifdef __cplusplus +} +#endif + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_ diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h index cc0e800..3d0556b 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -25,8 +25,8 @@ ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _EMBEDDED_RSA_H_ -#define _EMBEDDED_RSA_H_ +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ #include <inttypes.h> @@ -55,4 +55,4 @@ int RSA_verify(const RSAPublicKey *key, } #endif -#endif +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h index 120ddcb..ef60aab 100644 --- a/include/mincrypt/sha.h +++ b/include/mincrypt/sha.h @@ -1,8 +1,30 @@ -// Copyright 2005 Google Inc. All Rights Reserved. -// Author: mschilder@google.com (Marius Schilder) - -#ifndef SECURITY_UTIL_LITE_SHA1_H__ -#define SECURITY_UTIL_LITE_SHA1_H__ +/* + * Copyright 2005 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ #include <stdint.h> #include "hash-internal.h" @@ -27,4 +49,4 @@ const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest); } #endif // __cplusplus -#endif // SECURITY_UTIL_LITE_SHA1_H__ +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ diff --git a/include/mincrypt/sha256.h b/include/mincrypt/sha256.h index 0f3efb7..3a87c31 100644 --- a/include/mincrypt/sha256.h +++ b/include/mincrypt/sha256.h @@ -1,8 +1,31 @@ -// Copyright 2011 Google Inc. All Rights Reserved. -// Author: mschilder@google.com (Marius Schilder) - -#ifndef SECURITY_UTIL_LITE_SHA256_H__ -#define SECURITY_UTIL_LITE_SHA256_H__ +/* + * Copyright 2011 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_ #include <stdint.h> #include "hash-internal.h" @@ -26,4 +49,4 @@ const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest); } #endif // __cplusplus -#endif // SECURITY_UTIL_LITE_SHA256_H__ +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_ diff --git a/init/Android.mk b/init/Android.mk index abfc68a..1f43ba6 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -10,7 +10,6 @@ LOCAL_SRC_FILES:= \ property_service.c \ util.c \ parser.c \ - logo.c \ keychords.c \ signal_handler.c \ init_parser.c \ diff --git a/init/builtins.c b/init/builtins.c index e8c8f91..e2932d5 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -797,12 +797,24 @@ int do_chmod(int nargs, char **args) { int do_restorecon(int nargs, char **args) { int i; + int ret = 0; for (i = 1; i < nargs; i++) { if (restorecon(args[i]) < 0) - return -errno; + ret = -errno; } - return 0; + return ret; +} + +int do_restorecon_recursive(int nargs, char **args) { + int i; + int ret = 0; + + for (i = 1; i < nargs; i++) { + if (restorecon_recursive(args[i]) < 0) + ret = -errno; + } + return ret; } int do_setsebool(int nargs, char **args) { diff --git a/init/devices.c b/init/devices.c index 1893642..af88c5f 100644 --- a/init/devices.c +++ b/init/devices.c @@ -33,6 +33,7 @@ #include <selinux/selinux.h> #include <selinux/label.h> #include <selinux/android.h> +#include <selinux/avc.h> #include <private/android_filesystem_config.h> #include <sys/time.h> @@ -830,6 +831,15 @@ void handle_device_fd() struct uevent uevent; parse_event(msg, &uevent); + if (sehandle && selinux_status_updated() > 0) { + struct selabel_handle *sehandle2; + sehandle2 = selinux_android_file_context_handle(); + if (sehandle2) { + selabel_close(sehandle); + sehandle = sehandle2; + } + } + handle_device_event(&uevent); handle_firmware_event(&uevent); } @@ -896,6 +906,7 @@ void device_init(void) sehandle = NULL; if (is_selinux_enabled() > 0) { sehandle = selinux_android_file_context_handle(); + selinux_status_open(true); } /* is 256K enough? udev uses 16MB! */ diff --git a/init/init.c b/init/init.c index 94a2011..864fc6c 100644 --- a/init/init.c +++ b/init/init.c @@ -250,14 +250,12 @@ void service_start(struct service *svc, const char *dynamic_args) for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); - setsockcreatecon(scon); - for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); int s = create_socket(si->name, socket_type, - si->perm, si->uid, si->gid); + si->perm, si->uid, si->gid, si->socketcon ?: scon); if (s >= 0) { publish_socket(si->name, s); } @@ -265,7 +263,6 @@ void service_start(struct service *svc, const char *dynamic_args) freecon(scon); scon = NULL; - setsockcreatecon(NULL); if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { @@ -657,29 +654,28 @@ static int console_init_action(int nargs, char **args) have_console = 1; close(fd); - if( load_565rle_image(INIT_IMAGE_FILE) ) { - fd = open("/dev/tty0", O_WRONLY); - if (fd >= 0) { - const char *msg; - msg = "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" // console is 40 cols x 30 lines - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - " A N D R O I D "; - write(fd, msg, strlen(msg)); - close(fd); - } + fd = open("/dev/tty0", O_WRONLY); + if (fd >= 0) { + const char *msg; + msg = "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" // console is 40 cols x 30 lines + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " A N D R O I D "; + write(fd, msg, strlen(msg)); + close(fd); } + return 0; } diff --git a/init/init.h b/init/init.h index aa6a4ab..736b75b 100644 --- a/init/init.h +++ b/init/init.h @@ -55,6 +55,7 @@ struct socketinfo { uid_t uid; gid_t gid; int perm; + const char *socketcon; }; struct svcenvinfo { @@ -132,10 +133,6 @@ void service_restart(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); -#define INIT_IMAGE_FILE "/initlogo.rle" - -int load_565rle_image( char *file_name ); - extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; extern int selinux_reload_policy(void); diff --git a/init/init_parser.c b/init/init_parser.c index 776c699..3f0838f 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -135,6 +135,7 @@ int lookup_keyword(const char *s) case 'r': if (!strcmp(s, "estart")) return K_restart; if (!strcmp(s, "estorecon")) return K_restorecon; + if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive; if (!strcmp(s, "mdir")) return K_rmdir; if (!strcmp(s, "m")) return K_rm; break; @@ -552,12 +553,14 @@ void queue_all_property_triggers() if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); } else { + int ret; memcpy(prop_name, name, length); prop_name[length] = 0; /* does the property exist, and match the trigger value? */ - property_get(prop_name, value); - if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) { + ret = property_get(prop_name, value); + if (ret > 0 && (!strcmp(equals + 1, value) || + !strcmp(equals + 1, "*"))) { action_add_queue_tail(act); } } @@ -771,7 +774,7 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args svc->envvars = ei; break; } - case K_socket: {/* name type perm [ uid gid ] */ + case K_socket: {/* name type perm [ uid gid context ] */ struct socketinfo *si; if (nargs < 4) { parse_error(state, "socket option requires name, type, perm arguments\n"); @@ -794,6 +797,8 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args si->uid = decode_uid(args[4]); if (nargs > 5) si->gid = decode_uid(args[5]); + if (nargs > 6) + si->socketcon = args[6]; si->next = svc->sockets; svc->sockets = si; break; diff --git a/init/keywords.h b/init/keywords.h index 5a44df3..97fe50c 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -17,6 +17,7 @@ int do_mount(int nargs, char **args); int do_powerctl(int nargs, char **args); int do_restart(int nargs, char **args); int do_restorecon(int nargs, char **args); +int do_restorecon_recursive(int nargs, char **args); int do_rm(int nargs, char **args); int do_rmdir(int nargs, char **args); int do_setcon(int nargs, char **args); @@ -71,6 +72,7 @@ enum { KEYWORD(powerctl, COMMAND, 1, do_powerctl) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(restorecon, COMMAND, 1, do_restorecon) + KEYWORD(restorecon_recursive, COMMAND, 1, do_restorecon_recursive) KEYWORD(rm, COMMAND, 1, do_rm) KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(seclabel, OPTION, 0, 0) diff --git a/init/logo.c b/init/logo.c deleted file mode 100644 index 614224c..0000000 --- a/init/logo.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2008 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <linux/fb.h> -#include <linux/kd.h> - -#include "log.h" - -#ifdef ANDROID -#include <cutils/memory.h> -#else -void android_memset16(void *_ptr, unsigned short val, unsigned count) -{ - unsigned short *ptr = _ptr; - count >>= 1; - while(count--) - *ptr++ = val; -} -#endif - -struct FB { - unsigned short *bits; - unsigned size; - int fd; - struct fb_fix_screeninfo fi; - struct fb_var_screeninfo vi; -}; - -#define fb_width(fb) ((fb)->vi.xres) -#define fb_height(fb) ((fb)->vi.yres) -#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) - -static int fb_open(struct FB *fb) -{ - fb->fd = open("/dev/graphics/fb0", O_RDWR); - if (fb->fd < 0) - return -1; - - if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) - goto fail; - if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) - goto fail; - - fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, - MAP_SHARED, fb->fd, 0); - if (fb->bits == MAP_FAILED) - goto fail; - - return 0; - -fail: - close(fb->fd); - return -1; -} - -static void fb_close(struct FB *fb) -{ - munmap(fb->bits, fb_size(fb)); - close(fb->fd); -} - -/* there's got to be a more portable way to do this ... */ -static void fb_update(struct FB *fb) -{ - fb->vi.yoffset = 1; - ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); - fb->vi.yoffset = 0; - ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); -} - -static int vt_set_mode(int graphics) -{ - int fd, r; - fd = open("/dev/tty0", O_RDWR | O_SYNC); - if (fd < 0) - return -1; - r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); - close(fd); - return r; -} - -/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ - -int load_565rle_image(char *fn) -{ - struct FB fb; - struct stat s; - unsigned short *data, *bits, *ptr; - unsigned count, max; - int fd; - - if (vt_set_mode(1)) - return -1; - - fd = open(fn, O_RDONLY); - if (fd < 0) { - ERROR("cannot open '%s'\n", fn); - goto fail_restore_text; - } - - if (fstat(fd, &s) < 0) { - goto fail_close_file; - } - - data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) - goto fail_close_file; - - if (fb_open(&fb)) - goto fail_unmap_data; - - max = fb_width(&fb) * fb_height(&fb); - ptr = data; - count = s.st_size; - bits = fb.bits; - while (count > 3) { - unsigned n = ptr[0]; - if (n > max) - break; - android_memset16(bits, ptr[1], n << 1); - bits += n; - max -= n; - ptr += 2; - count -= 4; - } - - munmap(data, s.st_size); - fb_update(&fb); - fb_close(&fb); - close(fd); - unlink(fn); - return 0; - -fail_unmap_data: - munmap(data, s.st_size); -fail_close_file: - close(fd); -fail_restore_text: - vt_set_mode(0); - return -1; -} - diff --git a/init/property_service.c b/init/property_service.c index 9ac2781..c370769 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -81,6 +81,7 @@ struct { { "sys.powerctl", AID_SHELL, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, + { "gps.", AID_GPS, 0 }, { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, @@ -92,6 +93,7 @@ struct { { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, + { "persist.gps.", AID_GPS, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, { NULL, 0, 0 } @@ -437,10 +439,13 @@ void get_property_workspace(int *fd, int *sz) *sz = pa_workspace.size; } -static void load_properties(char *data) +static void load_properties(char *data, char *prefix) { char *key, *value, *eol, *sol, *tmp; + size_t plen; + if (prefix) + plen = strlen(prefix); sol = data; while((eol = strchr(sol, '\n'))) { key = sol; @@ -456,6 +461,9 @@ static void load_properties(char *data) tmp = value - 2; while((tmp > key) && isspace(*tmp)) *tmp-- = 0; + if (prefix && strncmp(key, prefix, plen)) + continue; + while(isspace(*value)) value++; tmp = eol - 2; while((tmp > value) && isspace(*tmp)) *tmp-- = 0; @@ -464,7 +472,7 @@ static void load_properties(char *data) } } -static void load_properties_from_file(const char *fn) +static void load_properties_from_file(const char *fn, char *prefix) { char *data; unsigned sz; @@ -472,7 +480,7 @@ static void load_properties_from_file(const char *fn) data = read_file(fn, &sz); if(data != 0) { - load_properties(data); + load_properties(data, prefix); free(data); } } @@ -545,7 +553,7 @@ void property_init(void) void property_load_boot_defaults(void) { - load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); + load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL); } int properties_inited(void) @@ -560,7 +568,7 @@ static void load_override_properties() { ret = property_get("ro.debuggable", debuggable); if (ret && (strcmp(debuggable, "1") == 0)) { - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL); } #endif /* ALLOW_LOCAL_PROP_OVERRIDE */ } @@ -582,13 +590,14 @@ void start_property_service(void) { int fd; - load_properties_from_file(PROP_PATH_SYSTEM_BUILD); - load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); + load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); + load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL); + load_properties_from_file(PROP_PATH_FACTORY, "ro."); load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); - fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); + fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL); if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); diff --git a/init/readme.txt b/init/readme.txt index 7a5997d..42a09cb 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -70,10 +70,13 @@ disabled setenv <name> <value> Set the environment variable <name> to <value> in the launched process. -socket <name> <type> <perm> [ <user> [ <group> ] ] +socket <name> <type> <perm> [ <user> [ <group> [ <context> ] ] ] Create a unix domain socket named /dev/socket/<name> and pass its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket". User and group default to 0. + Context is the SELinux security context for the socket. + It defaults to the service security context, as specified by seclabel or + computed based on the service executable file security context. user <username> Change to username before exec'ing this service. @@ -189,12 +192,18 @@ mount <type> <device> <dir> [ <mountoption> ]* device by name. <mountoption>s include "ro", "rw", "remount", "noatime", ... -restorecon <path> +restorecon <path> [ <path> ]* Restore the file named by <path> to the security context specified in the file_contexts configuration. Not required for directories created by the init.rc as these are automatically labeled correctly by init. +restorecon_recursive <path> [ <path> ]* + Recursively restore the directory tree named by <path> to the + security contexts specified in the file_contexts configuration. + Do NOT use this with paths leading to shell-writable or app-writable + directories, e.g. /data/local/tmp, /data/data or any prefix thereof. + setcon <securitycontext> Set the current process security context to the specified string. This is typically only used from early-init to set the init context diff --git a/init/util.c b/init/util.c index 1908b3a..9aaa77d 100644 --- a/init/util.c +++ b/init/util.c @@ -84,11 +84,15 @@ unsigned int decode_uid(const char *s) * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */ -int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) +int create_socket(const char *name, int type, mode_t perm, uid_t uid, + gid_t gid, const char *socketcon) { struct sockaddr_un addr; int fd, ret; - char *secon; + char *filecon; + + if (socketcon) + setsockcreatecon(socketcon); fd = socket(PF_UNIX, type, 0); if (fd < 0) { @@ -96,6 +100,9 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) return -1; } + if (socketcon) + setsockcreatecon(NULL); + memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", @@ -107,11 +114,11 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_close; } - secon = NULL; + filecon = NULL; if (sehandle) { - ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); + ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK); if (ret == 0) - setfscreatecon(secon); + setfscreatecon(filecon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); @@ -121,7 +128,7 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) } setfscreatecon(NULL); - freecon(secon); + freecon(filecon); chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); @@ -398,7 +405,9 @@ void open_devnull_stdio(void) void get_hardware_name(char *hardware, unsigned int *revision) { - char data[1024]; + const char *cpuinfo = "/proc/cpuinfo"; + char *data = NULL; + size_t len = 0, limit = 1024; int fd, n; char *x, *hw, *rev; @@ -406,14 +415,32 @@ void get_hardware_name(char *hardware, unsigned int *revision) if (hardware[0]) return; - fd = open("/proc/cpuinfo", O_RDONLY); + fd = open(cpuinfo, O_RDONLY); if (fd < 0) return; - n = read(fd, data, 1023); - close(fd); - if (n < 0) return; + for (;;) { + x = realloc(data, limit); + if (!x) { + ERROR("Failed to allocate memory to read %s\n", cpuinfo); + goto done; + } + data = x; + + n = read(fd, data + len, limit - len); + if (n < 0) { + ERROR("Failed reading %s: %s (%d)\n", cpuinfo, strerror(errno), errno); + goto done; + } + len += n; - data[n] = 0; + if (len < limit) + break; + + /* We filled the buffer, so increase size and loop to read more */ + limit *= 2; + } + + data[len] = 0; hw = strstr(data, "\nHardware"); rev = strstr(data, "\nRevision"); @@ -438,18 +465,22 @@ void get_hardware_name(char *hardware, unsigned int *revision) *revision = strtoul(x + 2, 0, 16); } } + +done: + close(fd); + free(data); } void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)) { - char cmdline[1024]; + char cmdline[2048]; char *ptr; int fd; fd = open("/proc/cmdline", O_RDONLY); if (fd >= 0) { - int n = read(fd, cmdline, 1023); + int n = read(fd, cmdline, sizeof(cmdline) - 1); if (n < 0) n = 0; /* get rid of trailing newline, it happens */ diff --git a/init/util.h b/init/util.h index 6bca4e6..04b8129 100644 --- a/init/util.h +++ b/init/util.h @@ -26,7 +26,7 @@ static const char *coldboot_done = "/dev/.coldboot_done"; int mtd_name_to_number(const char *name); int create_socket(const char *name, int type, mode_t perm, - uid_t uid, gid_t gid); + uid_t uid, gid_t gid, const char *socketcon); void *read_file(const char *fn, unsigned *_sz); time_t gettime(void); unsigned int decode_uid(const char *s); diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk new file mode 100644 index 0000000..66d7e62 --- /dev/null +++ b/libbacktrace/Android.mk @@ -0,0 +1,265 @@ +LOCAL_PATH:= $(call my-dir) + +common_src := \ + Backtrace.cpp \ + BacktraceThread.cpp \ + map_info.c \ + thread_utils.c \ + +common_cflags := \ + -Wall \ + -Wno-unused-parameter \ + -Werror \ + +common_conlyflags := \ + -std=gnu99 \ + +common_cppflags := \ + -std=gnu++11 \ + +common_shared_libs := \ + libcutils \ + libgccdemangle \ + liblog \ + +# To enable using libunwind on each arch, add it to the list below. +ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),)) + +#---------------------------------------------------------------------------- +# The native libbacktrace library with libunwind. +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + $(common_src) \ + UnwindCurrent.cpp \ + UnwindPtrace.cpp \ + +LOCAL_CFLAGS := \ + $(common_cflags) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_MODULE := libbacktrace +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + external/libunwind/include \ + +LOCAL_SHARED_LIBRARIES := \ + $(common_shared_libs) \ + libunwind \ + libunwind-ptrace \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include external/stlport/libstlport.mk + +include $(BUILD_SHARED_LIBRARY) + +else + +#---------------------------------------------------------------------------- +# The native libbacktrace library with libcorkscrew. +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + $(common_src) \ + Corkscrew.cpp \ + +LOCAL_CFLAGS := \ + $(common_cflags) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_MODULE := libbacktrace +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + system/core/libcorkscrew \ + +LOCAL_SHARED_LIBRARIES := \ + $(common_shared_libs) \ + libcorkscrew \ + libdl \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include external/stlport/libstlport.mk + +include $(BUILD_SHARED_LIBRARY) + +endif + +#---------------------------------------------------------------------------- +# libbacktrace test library, all optimizations turned off +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libbacktrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_testlib.c + +LOCAL_CFLAGS += \ + -std=gnu99 \ + -O0 \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_SHARED_LIBRARY) + +#---------------------------------------------------------------------------- +# libbacktrace test executable +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := backtrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_test.cpp \ + thread_utils.c \ + +LOCAL_CFLAGS += \ + -fno-builtin \ + -fstack-protector-all \ + -O0 \ + -g \ + -DGTEST_OS_LINUX_ANDROID \ + -DGTEST_HAS_STD_STRING \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + -fpermissive \ + +LOCAL_SHARED_LIBRARIES += \ + libcutils \ + libbacktrace_test \ + libbacktrace \ + +LOCAL_LDLIBS := \ + -lpthread \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_NATIVE_TEST) + +#---------------------------------------------------------------------------- +# Only linux-x86 host versions of libbacktrace supported. +#---------------------------------------------------------------------------- +ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) + +#---------------------------------------------------------------------------- +# The host libbacktrace library using libcorkscrew +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_SRC_FILES += \ + $(common_src) \ + Corkscrew.cpp \ + +LOCAL_CFLAGS += \ + $(common_cflags) \ + +LOCAL_CONLYFLAGS += \ + $(common_conlyflags) \ + +LOCAL_CPPFLAGS += \ + $(common_cppflags) \ + +LOCAL_C_INCLUDES := \ + $(common_c_includes) \ + system/core/libcorkscrew \ + +LOCAL_SHARED_LIBRARIES := \ + libgccdemangle \ + liblog \ + libcorkscrew \ + +LOCAL_LDLIBS += \ + -ldl \ + -lrt \ + +LOCAL_MODULE := libbacktrace +LOCAL_MODULE_TAGS := optional + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_SHARED_LIBRARY) + +#---------------------------------------------------------------------------- +# libbacktrace host test library, all optimizations turned off +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libbacktrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_testlib.c + +LOCAL_CFLAGS += \ + -std=gnu99 \ + -O0 \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_SHARED_LIBRARY) + +#---------------------------------------------------------------------------- +# libbacktrace host test executable +#---------------------------------------------------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := backtrace_test +LOCAL_MODULE_FLAGS := debug + +LOCAL_SRC_FILES := \ + backtrace_test.cpp \ + thread_utils.c \ + +LOCAL_CFLAGS += \ + -fno-builtin \ + -fstack-protector-all \ + -O0 \ + -g \ + -DGTEST_HAS_STD_STRING \ + +LOCAL_SHARED_LIBRARIES := \ + libbacktrace_test \ + libbacktrace \ + +LOCAL_CPPFLAGS += \ + -fpermissive \ + +LOCAL_LDLIBS := \ + -lpthread \ + +LOCAL_ADDITIONAL_DEPENDENCIES := \ + $(LOCAL_PATH)/Android.mk + +include $(BUILD_HOST_NATIVE_TEST) + +endif # HOST_OS-HOST_ARCH == linux-x86 diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp new file mode 100644 index 0000000..17d9e1d --- /dev/null +++ b/libbacktrace/Backtrace.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2013 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 <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <unistd.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <string> + +#include <backtrace/Backtrace.h> +#include <cutils/log.h> + +#include "Backtrace.h" +#include "thread_utils.h" + +//------------------------------------------------------------------------- +// BacktraceImpl functions. +//------------------------------------------------------------------------- +backtrace_t* BacktraceImpl::GetBacktraceData() { + return &backtrace_obj_->backtrace_; +} + +//------------------------------------------------------------------------- +// Backtrace functions. +//------------------------------------------------------------------------- +Backtrace::Backtrace(BacktraceImpl* impl) : impl_(impl), map_info_(NULL) { + impl_->SetParent(this); + backtrace_.num_frames = 0; + backtrace_.pid = -1; + backtrace_.tid = -1; +} + +Backtrace::~Backtrace() { + for (size_t i = 0; i < NumFrames(); i++) { + if (backtrace_.frames[i].func_name) { + free(backtrace_.frames[i].func_name); + backtrace_.frames[i].func_name = NULL; + } + } + + if (map_info_) { + backtrace_destroy_map_info_list(map_info_); + map_info_ = NULL; + } + + if (impl_) { + delete impl_; + impl_ = NULL; + } +} + +bool Backtrace::Unwind(size_t num_ignore_frames) { + return impl_->Unwind(num_ignore_frames); +} + +extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len, + int* status); + +std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) { + std::string func_name = impl_->GetFunctionNameRaw(pc, offset); + if (!func_name.empty()) { +#if defined(__APPLE__) + // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. + if (symbol_name[0] != '_') { + return func_name; + } +#endif + char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0); + if (name) { + func_name = name; + free(name); + } + } + return func_name; +} + +bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) { + if (ptr & 3) { + BACK_LOGW("invalid pointer %p", (void*)ptr); + *out_value = (uint32_t)-1; + return false; + } + return true; +} + +const char* Backtrace::GetMapName(uintptr_t pc, uintptr_t* map_start) { + const backtrace_map_info_t* map_info = FindMapInfo(pc); + if (map_info) { + if (map_start) { + *map_start = map_info->start; + } + return map_info->name; + } + return NULL; +} + +const backtrace_map_info_t* Backtrace::FindMapInfo(uintptr_t ptr) { + return backtrace_find_map_info(map_info_, ptr); +} + +std::string Backtrace::FormatFrameData(size_t frame_num) { + backtrace_frame_data_t* frame = &backtrace_.frames[frame_num]; + const char* map_name; + if (frame->map_name) { + map_name = frame->map_name; + } else { + map_name = "<unknown>"; + } + uintptr_t relative_pc; + if (frame->map_offset) { + relative_pc = frame->map_offset; + } else { + relative_pc = frame->pc; + } + + char buf[512]; + if (frame->func_name && frame->func_offset) { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")", + frame_num, (int)sizeof(uintptr_t)*2, relative_pc, map_name, + frame->func_name, frame->func_offset); + } else if (frame->func_name) { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame_num, + (int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name); + } else { + snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame_num, + (int)sizeof(uintptr_t)*2, relative_pc, map_name); + } + + return buf; +} + +//------------------------------------------------------------------------- +// BacktraceCurrent functions. +//------------------------------------------------------------------------- +BacktraceCurrent::BacktraceCurrent(BacktraceImpl* impl) : Backtrace(impl) { + map_info_ = backtrace_create_map_info_list(-1); + + backtrace_.pid = getpid(); +} + +BacktraceCurrent::~BacktraceCurrent() { +} + +bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + + const backtrace_map_info_t* map_info = FindMapInfo(ptr); + if (map_info && map_info->is_readable) { + *out_value = *reinterpret_cast<uint32_t*>(ptr); + return true; + } else { + BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr)); + *out_value = static_cast<uint32_t>(-1); + return false; + } +} + +//------------------------------------------------------------------------- +// BacktracePtrace functions. +//------------------------------------------------------------------------- +BacktracePtrace::BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid) + : Backtrace(impl) { + map_info_ = backtrace_create_map_info_list(tid); + + backtrace_.pid = pid; + backtrace_.tid = tid; +} + +BacktracePtrace::~BacktracePtrace() { +} + +bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) { + if (!VerifyReadWordArgs(ptr, out_value)) { + return false; + } + +#if defined(__APPLE__) + BACK_LOGW("MacOS does not support reading from another pid."); + return false; +#else + // ptrace() returns -1 and sets errno when the operation fails. + // To disambiguate -1 from a valid result, we clear errno beforehand. + errno = 0; + *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL); + if (*out_value == static_cast<uint32_t>(-1) && errno) { + BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s", + reinterpret_cast<void*>(ptr), Tid(), strerror(errno)); + return false; + } + return true; +#endif +} + +Backtrace* Backtrace::Create(pid_t pid, pid_t tid) { + if (pid < 0 || pid == getpid()) { + if (tid < 0 || tid == gettid()) { + return CreateCurrentObj(); + } else { + return CreateThreadObj(tid); + } + } else if (tid < 0) { + return CreatePtraceObj(pid, pid); + } else { + return CreatePtraceObj(pid, tid); + } +} + +//------------------------------------------------------------------------- +// Common interface functions. +//------------------------------------------------------------------------- +bool backtrace_create_context( + backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames) { + Backtrace* backtrace = Backtrace::Create(pid, tid); + if (!backtrace) { + return false; + } + if (!backtrace->Unwind(num_ignore_frames)) { + delete backtrace; + return false; + } + + context->data = backtrace; + context->backtrace = backtrace->GetBacktrace(); + return true; +} + +void backtrace_destroy_context(backtrace_context_t* context) { + if (context->data) { + Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data); + delete backtrace; + context->data = NULL; + } + context->backtrace = NULL; +} + +const backtrace_t* backtrace_get_data(backtrace_context_t* context) { + if (context && context->data) { + Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data); + return backtrace->GetBacktrace(); + } + return NULL; +} + +bool backtrace_read_word(const backtrace_context_t* context, uintptr_t ptr, uint32_t* value) { + if (context->data) { + Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data); + return backtrace->ReadWord(ptr, value); + } + return true; +} + +const char* backtrace_get_map_name(const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start) { + if (context->data) { + Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data); + return backtrace->GetMapName(pc, map_start); + } + return NULL; +} + +char* backtrace_get_func_name(const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset) { + if (context->data) { + Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data); + std::string func_name = backtrace->GetFunctionName(pc, func_offset); + if (!func_name.empty()) { + return strdup(func_name.c_str()); + } + } + return NULL; +} + +void backtrace_format_frame_data( + const backtrace_context_t* context, size_t frame_num, char* buf, + size_t buf_size) { + if (buf_size == 0 || buf == NULL) { + BACK_LOGW("bad call buf %p buf_size %zu", buf, buf_size); + } else if (context->data) { + Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data); + std::string line = backtrace->FormatFrameData(frame_num); + if (line.size() > buf_size) { + memcpy(buf, line.c_str(), buf_size-1); + buf[buf_size] = '\0'; + } else { + memcpy(buf, line.c_str(), line.size()+1); + } + } +} diff --git a/libbacktrace/Backtrace.h b/libbacktrace/Backtrace.h new file mode 100644 index 0000000..00f0a10 --- /dev/null +++ b/libbacktrace/Backtrace.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_H +#define _LIBBACKTRACE_BACKTRACE_H + +#include <backtrace/Backtrace.h> + +#include <sys/types.h> + +// Macro to log the function name along with the warning message. +#define BACK_LOGW(format, ...) \ + ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__) + +class BacktraceImpl { +public: + virtual ~BacktraceImpl() { } + + virtual bool Unwind(size_t num_ignore_frames) = 0; + + // The name returned is not demangled, Backtrace::GetFunctionName() + // takes care of demangling the name. + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0; + + void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; } + +protected: + backtrace_t* GetBacktraceData(); + + Backtrace* backtrace_obj_; +}; + +class BacktraceCurrent : public Backtrace { +public: + BacktraceCurrent(BacktraceImpl* impl); + virtual ~BacktraceCurrent(); + + bool ReadWord(uintptr_t ptr, uint32_t* out_value); +}; + +class BacktracePtrace : public Backtrace { +public: + BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid); + virtual ~BacktracePtrace(); + + bool ReadWord(uintptr_t ptr, uint32_t* out_value); +}; + +Backtrace* CreateCurrentObj(); +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid); +Backtrace* CreateThreadObj(pid_t tid); + +#endif // _LIBBACKTRACE_BACKTRACE_H diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp new file mode 100644 index 0000000..8e664c4 --- /dev/null +++ b/libbacktrace/BacktraceThread.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2013 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 <errno.h> +#include <inttypes.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include <sys/types.h> + +#include <cutils/atomic.h> +#include <cutils/log.h> + +#include "BacktraceThread.h" +#include "thread_utils.h" + +//------------------------------------------------------------------------- +// ThreadEntry implementation. +//------------------------------------------------------------------------- +static ThreadEntry* g_list = NULL; +static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; + +ThreadEntry::ThreadEntry( + BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) + : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL), + state(STATE_WAITING), num_ignore_frames(num_ignore_frames) { +} + +ThreadEntry::~ThreadEntry() { + pthread_mutex_lock(&g_entry_mutex); + if (g_list == this) { + g_list = next; + } else { + if (next) { + next->prev = prev; + } + prev->next = next; + } + pthread_mutex_unlock(&g_entry_mutex); + + next = NULL; + prev = NULL; +} + +ThreadEntry* ThreadEntry::AddThreadToUnwind( + BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) { + ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames); + + pthread_mutex_lock(&g_entry_mutex); + ThreadEntry* cur_entry = g_list; + while (cur_entry != NULL) { + if (cur_entry->Match(pid, tid)) { + // There is already an entry for this pid/tid, this is bad. + BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid); + + pthread_mutex_unlock(&g_entry_mutex); + return NULL; + } + cur_entry = cur_entry->next; + } + + // Add the entry to the list. + entry->next = g_list; + if (g_list) { + g_list->prev = entry; + } + g_list = entry; + pthread_mutex_unlock(&g_entry_mutex); + + return entry; +} + +//------------------------------------------------------------------------- +// BacktraceThread functions. +//------------------------------------------------------------------------- +static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo, + void* sigcontext) { + if (pthread_mutex_lock(&g_entry_mutex) == 0) { + pid_t pid = getpid(); + pid_t tid = gettid(); + ThreadEntry* cur_entry = g_list; + while (cur_entry) { + if (cur_entry->Match(pid, tid)) { + break; + } + cur_entry = cur_entry->next; + } + pthread_mutex_unlock(&g_entry_mutex); + if (!cur_entry) { + BACK_LOGW("Unable to find pid %d tid %d information", pid, tid); + return; + } + + if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) { + cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext, + cur_entry->num_ignore_frames); + } + android_atomic_release_store(STATE_DONE, &cur_entry->state); + } +} + +BacktraceThread::BacktraceThread( + BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid) + : BacktraceCurrent(impl), thread_intf_(thread_intf) { + backtrace_.tid = tid; +} + +BacktraceThread::~BacktraceThread() { +} + +void BacktraceThread::FinishUnwind() { + for (size_t i = 0; i < NumFrames(); i++) { + backtrace_frame_data_t* frame = &backtrace_.frames[i]; + + frame->map_offset = 0; + uintptr_t map_start; + frame->map_name = GetMapName(frame->pc, &map_start); + if (frame->map_name) { + frame->map_offset = frame->pc - map_start; + } + + frame->func_offset = 0; + std::string func_name = GetFunctionName(frame->pc, &frame->func_offset); + if (!func_name.empty()) { + frame->func_name = strdup(func_name.c_str()); + } + } +} + +bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { + entry->state = STATE_WAITING; + + if (tgkill(Pid(), Tid(), SIGURG) != 0) { + BACK_LOGW("tgkill failed %s", strerror(errno)); + return false; + } + + // Allow up to ten seconds for the dump to start. + int wait_millis = 10000; + int32_t state; + while (true) { + state = android_atomic_acquire_load(&entry->state); + if (state != STATE_WAITING) { + break; + } + if (wait_millis--) { + usleep(1000); + } else { + break; + } + } + + bool cancelled = false; + if (state == STATE_WAITING) { + if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) { + BACK_LOGW("Cancelled dump of thread %d", entry->tid); + state = STATE_CANCEL; + cancelled = true; + } else { + state = android_atomic_acquire_load(&entry->state); + } + } + + // Wait for at most ten seconds for the cancel or dump to finish. + wait_millis = 10000; + while (android_atomic_acquire_load(&entry->state) != STATE_DONE) { + if (wait_millis--) { + usleep(1000); + } else { + BACK_LOGW("Didn't finish thread unwind in 60 seconds."); + break; + } + } + return !cancelled; +} + +bool BacktraceThread::Unwind(size_t num_ignore_frames) { + if (!thread_intf_->Init()) { + return false; + } + + ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( + thread_intf_, Pid(), Tid(), num_ignore_frames); + if (!entry) { + return false; + } + + // Prevent multiple threads trying to set the trigger action on different + // threads at the same time. + bool retval = false; + if (pthread_mutex_lock(&g_sigaction_mutex) == 0) { + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalHandler; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&act.sa_mask); + if (sigaction(SIGURG, &act, &oldact) == 0) { + retval = TriggerUnwindOnThread(entry); + sigaction(SIGURG, &oldact, NULL); + } else { + BACK_LOGW("sigaction failed %s", strerror(errno)); + } + pthread_mutex_unlock(&g_sigaction_mutex); + } else { + BACK_LOGW("unable to acquire sigaction mutex."); + } + + if (retval) { + FinishUnwind(); + } + delete entry; + + return retval; +} diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h new file mode 100644 index 0000000..8ed1122 --- /dev/null +++ b/libbacktrace/BacktraceThread.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_BACKTRACE_THREAD_H +#define _LIBBACKTRACE_BACKTRACE_THREAD_H + +#include <inttypes.h> +#include <pthread.h> +#include <sys/types.h> + +#include "Backtrace.h" + +typedef enum { + STATE_WAITING = 0, + STATE_DUMPING, + STATE_DONE, + STATE_CANCEL, +} state_e; + +class BacktraceThreadInterface; + +struct ThreadEntry { + ThreadEntry( + BacktraceThreadInterface* impl, pid_t pid, pid_t tid, + size_t num_ignore_frames); + ~ThreadEntry(); + + bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); } + + static ThreadEntry* AddThreadToUnwind( + BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid, + size_t num_ignored_frames); + + BacktraceThreadInterface* thread_intf; + pid_t pid; + pid_t tid; + ThreadEntry* next; + ThreadEntry* prev; + int32_t state; + int num_ignore_frames; +}; + +// Interface class that does not contain any local storage, only defines +// virtual functions to be defined by subclasses. +class BacktraceThreadInterface { +public: + virtual ~BacktraceThreadInterface() { } + + virtual bool Init() = 0; + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0; +}; + +class BacktraceThread : public BacktraceCurrent { +public: + // impl and thread_intf should point to the same object, this allows + // the compiler to catch if an implementation does not properly + // subclass both. + BacktraceThread( + BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid); + virtual ~BacktraceThread(); + + virtual bool Unwind(size_t num_ignore_frames); + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { + thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames); + } + +private: + virtual bool TriggerUnwindOnThread(ThreadEntry* entry); + + virtual void FinishUnwind(); + + BacktraceThreadInterface* thread_intf_; +}; + +#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp new file mode 100644 index 0000000..9daa752 --- /dev/null +++ b/libbacktrace/Corkscrew.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "libbacktrace" + +#include <backtrace/backtrace.h> + +#include <string.h> + +#include <backtrace-arch.h> +#include <cutils/log.h> +#include <corkscrew/backtrace.h> + +#ifndef __USE_GNU +#define __USE_GNU +#endif +#include <dlfcn.h> + +#include "Corkscrew.h" + +//------------------------------------------------------------------------- +// CorkscrewCommon functions. +//------------------------------------------------------------------------- +bool CorkscrewCommon::GenerateFrameData( + backtrace_frame_t* cork_frames, ssize_t num_frames) { + if (num_frames < 0) { + BACK_LOGW("libcorkscrew unwind failed."); + return false; + } + + backtrace_t* data = GetBacktraceData(); + data->num_frames = num_frames; + for (size_t i = 0; i < data->num_frames; i++) { + backtrace_frame_data_t* frame = &data->frames[i]; + frame->pc = cork_frames[i].absolute_pc; + frame->sp = cork_frames[i].stack_top; + frame->stack_size = cork_frames[i].stack_size; + frame->map_name = NULL; + frame->map_offset = 0; + frame->func_name = NULL; + frame->func_offset = 0; + + uintptr_t map_start; + frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start); + if (frame->map_name) { + frame->map_offset = frame->pc - map_start; + } + + std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset); + if (!func_name.empty()) { + frame->func_name = strdup(func_name.c_str()); + } + } + return true; +} + +//------------------------------------------------------------------------- +// CorkscrewCurrent functions. +//------------------------------------------------------------------------- +CorkscrewCurrent::CorkscrewCurrent() { +} + +CorkscrewCurrent::~CorkscrewCurrent() { +} + +bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) { + backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; + ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES); + + return GenerateFrameData(frames, num_frames); +} + +std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + + // Get information about the current thread. + Dl_info info; + const backtrace_map_info_t* map_info = backtrace_obj_->FindMapInfo(pc); + const char* symbol_name = NULL; + if (map_info && dladdr((const void*)pc, &info) && info.dli_sname) { + *offset = pc - map_info->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase; + symbol_name = info.dli_sname; + + return symbol_name; + } + return ""; +} + +//------------------------------------------------------------------------- +// CorkscrewThread functions. +//------------------------------------------------------------------------- +CorkscrewThread::CorkscrewThread() { +} + +CorkscrewThread::~CorkscrewThread() { + if (corkscrew_map_info_) { + free_map_info_list(corkscrew_map_info_); + corkscrew_map_info_ = NULL; + } +} + +bool CorkscrewThread::Init() { + corkscrew_map_info_ = load_map_info_list(backtrace_obj_->Pid()); + return corkscrew_map_info_ != NULL; +} + +void CorkscrewThread::ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { + backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; + ssize_t num_frames = unwind_backtrace_signal_arch( + siginfo, sigcontext, corkscrew_map_info_, frames, num_ignore_frames, + MAX_BACKTRACE_FRAMES); + if (num_frames > 0) { + backtrace_t* data = GetBacktraceData(); + data->num_frames = num_frames; + for (size_t i = 0; i < data->num_frames; i++) { + backtrace_frame_data_t* frame = &data->frames[i]; + frame->pc = frames[i].absolute_pc; + frame->sp = frames[i].stack_top; + frame->stack_size = frames[i].stack_size; + + frame->map_offset = 0; + frame->map_name = NULL; + frame->map_offset = 0; + + frame->func_offset = 0; + frame->func_name = NULL; + } + } +} + +//------------------------------------------------------------------------- +// CorkscrewPtrace functions. +//------------------------------------------------------------------------- +CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) { +} + +CorkscrewPtrace::~CorkscrewPtrace() { + if (ptrace_context_) { + free_ptrace_context(ptrace_context_); + ptrace_context_ = NULL; + } +} + +bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) { + ptrace_context_ = load_ptrace_context(backtrace_obj_->Tid()); + + backtrace_frame_t frames[MAX_BACKTRACE_FRAMES]; + ssize_t num_frames = unwind_backtrace_ptrace( + backtrace_obj_->Tid(), ptrace_context_, frames, num_ignore_frames, + MAX_BACKTRACE_FRAMES); + + return GenerateFrameData(frames, num_frames); +} + +std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + // Get information about a different process. + const map_info_t* map_info; + const symbol_t* symbol; + find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol); + char* symbol_name = NULL; + if (symbol) { + if (map_info) { + *offset = pc - map_info->start - symbol->start; + } + symbol_name = symbol->name; + return symbol_name; + } + + return ""; +} + +//------------------------------------------------------------------------- +// C++ object creation functions. +//------------------------------------------------------------------------- +Backtrace* CreateCurrentObj() { + return new BacktraceCurrent(new CorkscrewCurrent()); +} + +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) { + return new BacktracePtrace(new CorkscrewPtrace(), pid, tid); +} + +Backtrace* CreateThreadObj(pid_t tid) { + CorkscrewThread* thread_obj = new CorkscrewThread(); + return new BacktraceThread(thread_obj, thread_obj, tid); +} diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h new file mode 100644 index 0000000..7cb125c --- /dev/null +++ b/libbacktrace/Corkscrew.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_CORKSCREW_H +#define _LIBBACKTRACE_CORKSCREW_H + +#include <inttypes.h> + +#include <string> + +#include <backtrace/backtrace.h> +#include <backtrace/Backtrace.h> + +#include <corkscrew/backtrace.h> + +#include "Backtrace.h" +#include "BacktraceThread.h" + +class CorkscrewCommon : public BacktraceImpl { +public: + bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames); +}; + +class CorkscrewCurrent : public CorkscrewCommon { +public: + CorkscrewCurrent(); + virtual ~CorkscrewCurrent(); + + virtual bool Unwind(size_t num_ignore_threads); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); +}; + +class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface { +public: + CorkscrewThread(); + virtual ~CorkscrewThread(); + + virtual bool Init(); + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); + +private: + map_info_t* corkscrew_map_info_; +}; + +class CorkscrewPtrace : public CorkscrewCommon { +public: + CorkscrewPtrace(); + virtual ~CorkscrewPtrace(); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + + virtual bool Unwind(size_t num_ignore_threads); + +private: + ptrace_context_t* ptrace_context_; +}; + +#endif // _LIBBACKTRACE_CORKSCREW_H diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp new file mode 100644 index 0000000..d4ba68f --- /dev/null +++ b/libbacktrace/UnwindCurrent.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "libbacktrace" + +#include <sys/types.h> + +#include <cutils/log.h> + +#include <backtrace/backtrace.h> + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +#include "UnwindCurrent.h" + +// Define the ucontext_t structures needed for each supported arch. +#if defined(__arm__) + // The current version of the <signal.h> doesn't define ucontext_t. + #include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined. + + // Machine context at the time a signal was raised. + typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + uint32_t uc_sigmask; + } ucontext_t; +#elif defined(__mips__) + typedef struct ucontext { + uint32_t sp; + uint32_t ra; + uint32_t pc; + } ucontext_t; +#elif defined(__i386__) + #include <asm/sigcontext.h> + #include <asm/ucontext.h> + typedef struct ucontext ucontext_t; +#else + #error Unsupported architecture. +#endif + +//------------------------------------------------------------------------- +// UnwindCurrent functions. +//------------------------------------------------------------------------- +UnwindCurrent::UnwindCurrent() { +} + +UnwindCurrent::~UnwindCurrent() { +} + +bool UnwindCurrent::Unwind(size_t num_ignore_frames) { + int ret = unw_getcontext(&context_); + if (ret < 0) { + BACK_LOGW("unw_getcontext failed %d", ret); + return false; + } + return UnwindFromContext(num_ignore_frames, true); +} + +std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + char buf[512]; + unw_word_t value; + if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf), + &value, &context_) >= 0 && buf[0] != '\0') { + *offset = static_cast<uintptr_t>(value); + return buf; + } + return ""; +} + +bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) { + backtrace_t* backtrace = GetBacktraceData(); + backtrace->num_frames = 0; + + // The cursor structure is pretty large, do not put it on the stack. + unw_cursor_t* cursor = new unw_cursor_t; + int ret = unw_init_local(cursor, &context_); + if (ret < 0) { + BACK_LOGW("unw_init_local failed %d", ret); + return false; + } + + do { + unw_word_t pc; + ret = unw_get_reg(cursor, UNW_REG_IP, &pc); + if (ret < 0) { + BACK_LOGW("Failed to read IP %d", ret); + break; + } + unw_word_t sp; + ret = unw_get_reg(cursor, UNW_REG_SP, &sp); + if (ret < 0) { + BACK_LOGW("Failed to read SP %d", ret); + break; + } + + if (num_ignore_frames == 0) { + size_t num_frames = backtrace->num_frames; + backtrace_frame_data_t* frame = &backtrace->frames[num_frames]; + frame->pc = static_cast<uintptr_t>(pc); + frame->sp = static_cast<uintptr_t>(sp); + frame->stack_size = 0; + frame->map_name = NULL; + frame->map_offset = 0; + frame->func_name = NULL; + frame->func_offset = 0; + + if (num_frames > 0) { + // Set the stack size for the previous frame. + backtrace_frame_data_t* prev = &backtrace->frames[num_frames-1]; + prev->stack_size = frame->sp - prev->sp; + } + + if (resolve) { + std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset); + if (!func_name.empty()) { + frame->func_name = strdup(func_name.c_str()); + } + + uintptr_t map_start; + frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start); + if (frame->map_name) { + frame->map_offset = frame->pc - map_start; + } + } + + backtrace->num_frames++; + } else { + num_ignore_frames--; + } + ret = unw_step (cursor); + } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES); + + delete cursor; + return true; +} + +void UnwindCurrent::ExtractContext(void* sigcontext) { + unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_); + const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext); + +#if defined(__arm__) + context->regs[0] = uc->uc_mcontext.arm_r0; + context->regs[1] = uc->uc_mcontext.arm_r1; + context->regs[2] = uc->uc_mcontext.arm_r2; + context->regs[3] = uc->uc_mcontext.arm_r3; + context->regs[4] = uc->uc_mcontext.arm_r4; + context->regs[5] = uc->uc_mcontext.arm_r5; + context->regs[6] = uc->uc_mcontext.arm_r6; + context->regs[7] = uc->uc_mcontext.arm_r7; + context->regs[8] = uc->uc_mcontext.arm_r8; + context->regs[9] = uc->uc_mcontext.arm_r9; + context->regs[10] = uc->uc_mcontext.arm_r10; + context->regs[11] = uc->uc_mcontext.arm_fp; + context->regs[12] = uc->uc_mcontext.arm_ip; + context->regs[13] = uc->uc_mcontext.arm_sp; + context->regs[14] = uc->uc_mcontext.arm_lr; + context->regs[15] = uc->uc_mcontext.arm_pc; +#elif defined(__mips__) + context->uc_mcontext.sp = uc->sp; + context->uc_mcontext.pc = uc->pc; + context->uc_mcontext.ra = uc->ra; +#elif defined(__i386__) + context->uc_mcontext.gregs[REG_EBP] = uc->uc_mcontext.gregs[REG_EBP]; + context->uc_mcontext.gregs[REG_ESP] = uc->uc_mcontext.gregs[REG_ESP]; + context->uc_mcontext.gregs[REG_EIP] = uc->uc_mcontext.gregs[REG_EIP]; +#endif +} + +//------------------------------------------------------------------------- +// UnwindThread functions. +//------------------------------------------------------------------------- +UnwindThread::UnwindThread() { +} + +UnwindThread::~UnwindThread() { +} + +bool UnwindThread::Init() { + return true; +} + +void UnwindThread::ThreadUnwind( + siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) { + ExtractContext(sigcontext); + UnwindFromContext(num_ignore_frames, false); +} + +//------------------------------------------------------------------------- +// C++ object creation function. +//------------------------------------------------------------------------- +Backtrace* CreateCurrentObj() { + return new BacktraceCurrent(new UnwindCurrent()); +} + +Backtrace* CreateThreadObj(pid_t tid) { + UnwindThread* thread_obj = new UnwindThread(); + return new BacktraceThread(thread_obj, thread_obj, tid); +} diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h new file mode 100644 index 0000000..7dc977d --- /dev/null +++ b/libbacktrace/UnwindCurrent.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_UNWIND_CURRENT_H +#define _LIBBACKTRACE_UNWIND_CURRENT_H + +#include <string> + +#include "Backtrace.h" +#include "BacktraceThread.h" + +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +class UnwindCurrent : public BacktraceImpl { +public: + UnwindCurrent(); + virtual ~UnwindCurrent(); + + virtual bool Unwind(size_t num_ignore_frames); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + + bool UnwindFromContext(size_t num_ignore_frames, bool resolve); + + void ExtractContext(void* sigcontext); + +protected: + unw_context_t context_; +}; + +class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface { +public: + UnwindThread(); + virtual ~UnwindThread(); + + virtual bool Init(); + + virtual void ThreadUnwind( + siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames); +}; + +#endif // _LIBBACKTRACE_UNWIND_CURRENT_H diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp new file mode 100644 index 0000000..a734a24 --- /dev/null +++ b/libbacktrace/UnwindPtrace.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "libbacktrace" + +#include <backtrace/backtrace.h> + +#include <sys/types.h> +#include <string.h> + +#include <cutils/log.h> + +#include <libunwind.h> +#include <libunwind-ptrace.h> + +#include "UnwindPtrace.h" + +UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) { +} + +UnwindPtrace::~UnwindPtrace() { + if (upt_info_) { + _UPT_destroy(upt_info_); + upt_info_ = NULL; + } + if (addr_space_) { + unw_destroy_addr_space(addr_space_); + addr_space_ = NULL; + } +} + +bool UnwindPtrace::Unwind(size_t num_ignore_frames) { + addr_space_ = unw_create_addr_space(&_UPT_accessors, 0); + if (!addr_space_) { + BACK_LOGW("unw_create_addr_space failed."); + return false; + } + + upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(backtrace_obj_->Tid())); + if (!upt_info_) { + BACK_LOGW("Failed to create upt info."); + return false; + } + + backtrace_t* backtrace = GetBacktraceData(); + backtrace->num_frames = 0; + + unw_cursor_t cursor; + int ret = unw_init_remote(&cursor, addr_space_, upt_info_); + if (ret < 0) { + BACK_LOGW("unw_init_remote failed %d", ret); + return false; + } + + do { + unw_word_t pc; + ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); + if (ret < 0) { + BACK_LOGW("Failed to read IP %d", ret); + break; + } + unw_word_t sp; + ret = unw_get_reg(&cursor, UNW_REG_SP, &sp); + if (ret < 0) { + BACK_LOGW("Failed to read SP %d", ret); + break; + } + + if (num_ignore_frames == 0) { + size_t num_frames = backtrace->num_frames; + backtrace_frame_data_t* frame = &backtrace->frames[num_frames]; + frame->pc = static_cast<uintptr_t>(pc); + frame->sp = static_cast<uintptr_t>(sp); + frame->stack_size = 0; + frame->map_name = NULL; + frame->map_offset = 0; + frame->func_name = NULL; + frame->func_offset = 0; + + if (num_frames > 0) { + backtrace_frame_data_t* prev = &backtrace->frames[num_frames-1]; + prev->stack_size = frame->sp - prev->sp; + } + + std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset); + if (!func_name.empty()) { + frame->func_name = strdup(func_name.c_str()); + } + + uintptr_t map_start; + frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start); + if (frame->map_name) { + frame->map_offset = frame->pc - map_start; + } + + backtrace->num_frames++; + } else { + num_ignore_frames--; + } + ret = unw_step (&cursor); + } while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES); + + return true; +} + +std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) { + *offset = 0; + char buf[512]; + unw_word_t value; + if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value, + upt_info_) >= 0 && buf[0] != '\0') { + *offset = static_cast<uintptr_t>(value); + return buf; + } + return ""; +} + +//------------------------------------------------------------------------- +// C++ object creation function. +//------------------------------------------------------------------------- +Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) { + return new BacktracePtrace(new UnwindPtrace(), pid, tid); +} diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h new file mode 100644 index 0000000..781405b --- /dev/null +++ b/libbacktrace/UnwindPtrace.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H +#define _LIBBACKTRACE_UNWIND_PTRACE_H + +#include <string> + +#include "Backtrace.h" + +#include <libunwind.h> + +class UnwindPtrace : public BacktraceImpl { +public: + UnwindPtrace(); + virtual ~UnwindPtrace(); + + virtual bool Unwind(size_t num_ignore_frames); + + virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset); + +private: + unw_addr_space_t addr_space_; + struct UPT_info* upt_info_; +}; + +#endif // _LIBBACKTRACE_UNWIND_PTRACE_H diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp new file mode 100644 index 0000000..48e2bdc --- /dev/null +++ b/libbacktrace/backtrace_test.cpp @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2013 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 <dirent.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> + +#include <backtrace/backtrace.h> + +#include <cutils/atomic.h> +#include <gtest/gtest.h> + +#include <vector> + +#include "thread_utils.h" + +// Number of microseconds per milliseconds. +#define US_PER_MSEC 1000 + +// Number of nanoseconds in a second. +#define NS_PER_SEC 1000000000ULL + +// Number of simultaneous dumping operations to perform. +#define NUM_THREADS 20 + +// Number of simultaneous threads running in our forked process. +#define NUM_PTRACE_THREADS 5 + +typedef struct { + pid_t tid; + int32_t state; + pthread_t threadId; +} thread_t; + +typedef struct { + thread_t thread; + backtrace_context_t context; + int32_t* now; + int32_t done; +} dump_thread_t; + +extern "C" { +// Prototypes for functions in the test library. +int test_level_one(int, int, int, int, void (*)(void*), void*); + +int test_recursive_call(int, void (*)(void*), void*); +} + +uint64_t NanoTime() { + struct timespec t = { 0, 0 }; + clock_gettime(CLOCK_MONOTONIC, &t); + return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec); +} + +void DumpFrames(const backtrace_context_t* context) { + if (context->backtrace->num_frames == 0) { + printf(" No frames to dump\n"); + } else { + char line[512]; + for (size_t i = 0; i < context->backtrace->num_frames; i++) { + backtrace_format_frame_data(context, i, line, sizeof(line)); + printf(" %s\n", line); + } + } +} + +void WaitForStop(pid_t pid) { + uint64_t start = NanoTime(); + + siginfo_t si; + while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) { + if ((NanoTime() - start) > NS_PER_SEC) { + printf("The process did not get to a stopping point in 1 second.\n"); + break; + } + usleep(US_PER_MSEC); + } +} + +bool ReadyLevelBacktrace(const backtrace_t* backtrace) { + // See if test_level_four is in the backtrace. + bool found = false; + for (size_t i = 0; i < backtrace->num_frames; i++) { + if (backtrace->frames[i].func_name != NULL && + strcmp(backtrace->frames[i].func_name, "test_level_four") == 0) { + found = true; + break; + } + } + + return found; +} + +void VerifyLevelDump(const backtrace_t* backtrace) { + ASSERT_GT(backtrace->num_frames, static_cast<size_t>(0)); + ASSERT_LT(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES)); + + // Look through the frames starting at the highest to find the + // frame we want. + size_t frame_num = 0; + for (size_t i = backtrace->num_frames-1; i > 2; i--) { + if (backtrace->frames[i].func_name != NULL && + strcmp(backtrace->frames[i].func_name, "test_level_one") == 0) { + frame_num = i; + break; + } + } + ASSERT_GT(frame_num, static_cast<size_t>(0)); + + ASSERT_TRUE(NULL != backtrace->frames[frame_num].func_name); + ASSERT_STREQ(backtrace->frames[frame_num].func_name, "test_level_one"); + ASSERT_TRUE(NULL != backtrace->frames[frame_num-1].func_name); + ASSERT_STREQ(backtrace->frames[frame_num-1].func_name, "test_level_two"); + ASSERT_TRUE(NULL != backtrace->frames[frame_num-2].func_name); + ASSERT_STREQ(backtrace->frames[frame_num-2].func_name, "test_level_three"); + ASSERT_TRUE(NULL != backtrace->frames[frame_num-3].func_name); + ASSERT_STREQ(backtrace->frames[frame_num-3].func_name, "test_level_four"); +} + +void VerifyLevelBacktrace(void*) { + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0)); + + VerifyLevelDump(context.backtrace); + + backtrace_destroy_context(&context); +} + +bool ReadyMaxBacktrace(const backtrace_t* backtrace) { + return (backtrace->num_frames == MAX_BACKTRACE_FRAMES); +} + +void VerifyMaxDump(const backtrace_t* backtrace) { + ASSERT_EQ(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES)); + // Verify that the last frame is our recursive call. + ASSERT_TRUE(NULL != backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name); + ASSERT_STREQ(backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name, + "test_recursive_call"); +} + +void VerifyMaxBacktrace(void*) { + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0)); + + VerifyMaxDump(context.backtrace); + + backtrace_destroy_context(&context); +} + +void ThreadSetState(void* data) { + thread_t* thread = reinterpret_cast<thread_t*>(data); + android_atomic_acquire_store(1, &thread->state); + volatile int i = 0; + while (thread->state) { + i++; + } +} + +void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(const backtrace_t*)) { + backtrace_context_t context; + + backtrace_create_context(&context, getpid(), tid, 0); + + VerifyFunc(context.backtrace); + + backtrace_destroy_context(&context); +} + +bool WaitForNonZero(int32_t* value, uint64_t seconds) { + uint64_t start = NanoTime(); + do { + if (android_atomic_acquire_load(value)) { + return true; + } + } while ((NanoTime() - start) < seconds * NS_PER_SEC); + return false; +} + +TEST(libbacktrace, local_trace) { + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0); +} + +void VerifyIgnoreFrames( + const backtrace_t* bt_all, const backtrace_t* bt_ign1, + const backtrace_t* bt_ign2, const char* cur_proc) { + EXPECT_EQ(bt_all->num_frames, bt_ign1->num_frames + 1); + EXPECT_EQ(bt_all->num_frames, bt_ign2->num_frames + 2); + + // Check all of the frames are the same > the current frame. + bool check = (cur_proc == NULL); + for (size_t i = 0; i < bt_ign2->num_frames; i++) { + if (check) { + EXPECT_EQ(bt_ign2->frames[i].pc, bt_ign1->frames[i+1].pc); + EXPECT_EQ(bt_ign2->frames[i].sp, bt_ign1->frames[i+1].sp); + EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_ign1->frames[i+1].stack_size); + + EXPECT_EQ(bt_ign2->frames[i].pc, bt_all->frames[i+2].pc); + EXPECT_EQ(bt_ign2->frames[i].sp, bt_all->frames[i+2].sp); + EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_all->frames[i+2].stack_size); + } + if (!check && bt_ign2->frames[i].func_name && + strcmp(bt_ign2->frames[i].func_name, cur_proc) == 0) { + check = true; + } + } +} + +void VerifyLevelIgnoreFrames(void*) { + backtrace_context_t all; + ASSERT_TRUE(backtrace_create_context(&all, -1, -1, 0)); + ASSERT_TRUE(all.backtrace != NULL); + + backtrace_context_t ign1; + ASSERT_TRUE(backtrace_create_context(&ign1, -1, -1, 1)); + ASSERT_TRUE(ign1.backtrace != NULL); + + backtrace_context_t ign2; + ASSERT_TRUE(backtrace_create_context(&ign2, -1, -1, 2)); + ASSERT_TRUE(ign2.backtrace != NULL); + + VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, + "VerifyLevelIgnoreFrames"); + + backtrace_destroy_context(&all); + backtrace_destroy_context(&ign1); + backtrace_destroy_context(&ign2); +} + +TEST(libbacktrace, local_trace_ignore_frames) { + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, NULL), 0); +} + +TEST(libbacktrace, local_max_trace) { + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0); +} + +void VerifyProcTest(pid_t pid, pid_t tid, + bool (*ReadyFunc)(const backtrace_t*), + void (*VerifyFunc)(const backtrace_t*)) { + pid_t ptrace_tid; + if (tid < 0) { + ptrace_tid = pid; + } else { + ptrace_tid = tid; + } + uint64_t start = NanoTime(); + bool verified = false; + do { + usleep(US_PER_MSEC); + if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) { + // Wait for the process to get to a stopping point. + WaitForStop(ptrace_tid); + + backtrace_context_t context; + ASSERT_TRUE(backtrace_create_context(&context, pid, tid, 0)); + if (ReadyFunc(context.backtrace)) { + VerifyFunc(context.backtrace); + verified = true; + } + backtrace_destroy_context(&context); + ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0); + } + // If 5 seconds have passed, then we are done. + } while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC); + ASSERT_TRUE(verified); +} + +TEST(libbacktrace, ptrace_trace) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + VerifyProcTest(pid, -1, ReadyLevelBacktrace, VerifyLevelDump); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +TEST(libbacktrace, ptrace_max_trace) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0); + exit(1); + } + VerifyProcTest(pid, -1, ReadyMaxBacktrace, VerifyMaxDump); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +void VerifyProcessIgnoreFrames(const backtrace_t* bt_all) { + pid_t pid = bt_all->pid; + + backtrace_context_t ign1; + ASSERT_TRUE(backtrace_create_context(&ign1, pid, -1, 1)); + ASSERT_TRUE(ign1.backtrace != NULL); + + backtrace_context_t ign2; + ASSERT_TRUE(backtrace_create_context(&ign2, pid, -1, 2)); + ASSERT_TRUE(ign2.backtrace != NULL); + + VerifyIgnoreFrames(bt_all, ign1.backtrace, ign2.backtrace, NULL); + + backtrace_destroy_context(&ign1); + backtrace_destroy_context(&ign2); +} + +TEST(libbacktrace, ptrace_ignore_frames) { + pid_t pid; + if ((pid = fork()) == 0) { + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + VerifyProcTest(pid, -1, ReadyLevelBacktrace, VerifyProcessIgnoreFrames); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +// Create a process with multiple threads and dump all of the threads. +void* PtraceThreadLevelRun(void*) { + EXPECT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + return NULL; +} + +void GetThreads(pid_t pid, std::vector<pid_t>* threads) { + // Get the list of tasks. + char task_path[128]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + + DIR* tasks_dir = opendir(task_path); + ASSERT_TRUE(tasks_dir != NULL); + struct dirent* entry; + while ((entry = readdir(tasks_dir)) != NULL) { + char* end; + pid_t tid = strtoul(entry->d_name, &end, 10); + if (*end == '\0') { + threads->push_back(tid); + } + } + closedir(tasks_dir); +} + +TEST(libbacktrace, ptrace_threads) { + pid_t pid; + if ((pid = fork()) == 0) { + for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0); + } + ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0); + exit(1); + } + + // Check to see that all of the threads are running before unwinding. + std::vector<pid_t> threads; + uint64_t start = NanoTime(); + do { + usleep(US_PER_MSEC); + threads.clear(); + GetThreads(pid, &threads); + } while ((threads.size() != NUM_PTRACE_THREADS + 1) && + ((NanoTime() - start) <= 5 * NS_PER_SEC)); + ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1)); + + ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0); + WaitForStop(pid); + for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) { + // Skip the current forked process, we only care about the threads. + if (pid == *it) { + continue; + } + VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump); + } + ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); + + kill(pid, SIGKILL); + int status; + ASSERT_EQ(waitpid(pid, &status, 0), pid); +} + +void VerifyLevelThread(void*) { + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0)); + + VerifyLevelDump(context.backtrace); + + backtrace_destroy_context(&context); +} + +TEST(libbacktrace, thread_current_level) { + ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, NULL), 0); +} + +void VerifyMaxThread(void*) { + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0)); + + VerifyMaxDump(context.backtrace); + + backtrace_destroy_context(&context); +} + +TEST(libbacktrace, thread_current_max) { + ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, NULL), 0); +} + +void* ThreadLevelRun(void* data) { + thread_t* thread = reinterpret_cast<thread_t*>(data); + + thread->tid = gettid(); + EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0); + return NULL; +} + +TEST(libbacktrace, thread_level_trace) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + // Save the current signal action and make sure it is restored afterwards. + struct sigaction cur_action; + ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0); + + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid,0)); + + VerifyLevelDump(context.backtrace); + + backtrace_destroy_context(&context); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); + + // Verify that the old action was restored. + struct sigaction new_action; + ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0); + EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction); + EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags); +} + +TEST(libbacktrace, thread_ignore_frames) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0); + + // Wait up to 2 seconds for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + backtrace_context_t all; + ASSERT_TRUE(backtrace_create_context(&all, getpid(), thread_data.tid, 0)); + + backtrace_context_t ign1; + ASSERT_TRUE(backtrace_create_context(&ign1, getpid(), thread_data.tid, 1)); + + backtrace_context_t ign2; + ASSERT_TRUE(backtrace_create_context(&ign2, getpid(), thread_data.tid, 2)); + + VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, NULL); + + backtrace_destroy_context(&all); + backtrace_destroy_context(&ign1); + backtrace_destroy_context(&ign2); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); +} + +void* ThreadMaxRun(void* data) { + thread_t* thread = reinterpret_cast<thread_t*>(data); + + thread->tid = gettid(); + EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0); + return NULL; +} + +TEST(libbacktrace, thread_max_trace) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + thread_t thread_data = { 0, 0, 0 }; + pthread_t thread; + ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0); + + // Wait for the tid to be set. + ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2)); + + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid, 0)); + + VerifyMaxDump(context.backtrace); + + backtrace_destroy_context(&context); + + // Tell the thread to exit its infinite loop. + android_atomic_acquire_store(0, &thread_data.state); +} + +void* ThreadDump(void* data) { + dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data); + while (true) { + if (android_atomic_acquire_load(dump->now)) { + break; + } + } + + dump->context.data = NULL; + dump->context.backtrace = NULL; + + // The status of the actual unwind will be checked elsewhere. + backtrace_create_context(&dump->context, getpid(), dump->thread.tid, 0); + + android_atomic_acquire_store(1, &dump->done); + + return NULL; +} + +TEST(libbacktrace, thread_multiple_dump) { + // Dump NUM_THREADS simultaneously. + std::vector<thread_t> runners(NUM_THREADS); + std::vector<dump_thread_t> dumpers(NUM_THREADS); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + for (size_t i = 0; i < NUM_THREADS; i++) { + // Launch the runners, they will spin in hard loops doing nothing. + runners[i].tid = 0; + runners[i].state = 0; + ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0); + } + + // Wait for tids to be set. + for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) { + ASSERT_TRUE(WaitForNonZero(&it->state, 10)); + } + + // Start all of the dumpers at once, they will spin until they are signalled + // to begin their dump run. + int32_t dump_now = 0; + for (size_t i = 0; i < NUM_THREADS; i++) { + dumpers[i].thread.tid = runners[i].tid; + dumpers[i].thread.state = 0; + dumpers[i].done = 0; + dumpers[i].now = &dump_now; + + ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0); + } + + // Start all of the dumpers going at once. + android_atomic_acquire_store(1, &dump_now); + + for (size_t i = 0; i < NUM_THREADS; i++) { + ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 10)); + + // Tell the runner thread to exit its infinite loop. + android_atomic_acquire_store(0, &runners[i].state); + + ASSERT_TRUE(dumpers[i].context.backtrace != NULL); + VerifyMaxDump(dumpers[i].context.backtrace); + backtrace_destroy_context(&dumpers[i].context); + } +} + +TEST(libbacktrace, format_test) { + backtrace_context_t context; + + ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0)); + ASSERT_TRUE(context.backtrace != NULL); + + backtrace_frame_data_t* frame = &context.backtrace->frames[1]; + backtrace_frame_data_t save_frame = *frame; + + memset(frame, 0, sizeof(backtrace_frame_data_t)); + char buf[512]; + backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); +#if defined(__LP64__) + EXPECT_STREQ(buf, "#01 pc 0000000000000000 <unknown>"); +#else + EXPECT_STREQ(buf, "#01 pc 00000000 <unknown>"); +#endif + + frame->pc = 0x12345678; + frame->map_name = "MapFake"; + backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); +#if defined(__LP64__) + EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake"); +#else + EXPECT_STREQ(buf, "#01 pc 12345678 MapFake"); +#endif + + frame->func_name = "ProcFake"; + backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); +#if defined(__LP64__) + EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake)"); +#else + EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake)"); +#endif + + frame->func_offset = 645; + backtrace_format_frame_data(&context, 1, buf, sizeof(buf)); +#if defined(__LP64__) + EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake+645)"); +#else + EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake+645)"); +#endif + + *frame = save_frame; + + backtrace_destroy_context(&context); +} diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.c new file mode 100644 index 0000000..d4d15db --- /dev/null +++ b/libbacktrace/backtrace_testlib.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 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 <stdio.h> + +int test_level_four(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + if (callback_func != NULL) { + callback_func(data); + } else { + while (1) { + } + } + return one + two + three + four; +} + +int test_level_three(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3; +} + +int test_level_two(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2; +} + +int test_level_one(int one, int two, int three, int four, + void (*callback_func)(void*), void* data) { + return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1; +} + +int test_recursive_call(int level, void (*callback_func)(void*), void* data) { + if (level > 0) { + return test_recursive_call(level - 1, callback_func, data) + level; + } else if (callback_func != NULL) { + callback_func(data); + } else { + while (1) { + } + } + return 0; +} diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c new file mode 100644 index 0000000..9cc6e01 --- /dev/null +++ b/libbacktrace/map_info.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <cutils/log.h> +#include <sys/time.h> + +#include <backtrace/backtrace.h> + +#if defined(__APPLE__) + +// Mac OS vmmap(1) output: +// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static backtrace_map_info_t* parse_vmmap_line(const char* line) { + unsigned long int start; + unsigned long int end; + char permissions[4]; + int name_pos; + if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", + &start, &end, permissions, &name_pos) != 3) { + return NULL; + } + + const char* name = line + name_pos; + size_t name_len = strlen(name); + + backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len); + if (mi != NULL) { + mi->start = start; + mi->end = end; + mi->is_readable = permissions[0] == 'r'; + mi->is_writable = permissions[1] == 'w'; + mi->is_executable = permissions[2] == 'x'; + memcpy(mi->name, name, name_len); + mi->name[name_len - 1] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) { + char cmd[1024]; + if (pid < 0) { + pid = getpid(); + } + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); + FILE* fp = popen(cmd, "r"); + if (fp == NULL) { + return NULL; + } + + char line[1024]; + backtrace_map_info_t* milist = NULL; + while (fgets(line, sizeof(line), fp) != NULL) { + backtrace_map_info_t* mi = parse_vmmap_line(line); + if (mi != NULL) { + mi->next = milist; + milist = mi; + } + } + pclose(fp); + return milist; +} + +#else + +// Linux /proc/<pid>/maps lines: +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static backtrace_map_info_t* parse_maps_line(const char* line) +{ + unsigned long int start; + unsigned long int end; + char permissions[5]; + int name_pos; + if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, + permissions, &name_pos) != 3) { + return NULL; + } + + while (isspace(line[name_pos])) { + name_pos += 1; + } + const char* name = line + name_pos; + size_t name_len = strlen(name); + if (name_len && name[name_len - 1] == '\n') { + name_len -= 1; + } + + backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1); + if (mi) { + mi->start = start; + mi->end = end; + mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; + mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w'; + mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; + memcpy(mi->name, name, name_len); + mi->name[name_len] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d, is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) { + char path[PATH_MAX]; + char line[1024]; + FILE* fp; + backtrace_map_info_t* milist = NULL; + + if (tid < 0) { + tid = getpid(); + } + snprintf(path, PATH_MAX, "/proc/%d/maps", tid); + fp = fopen(path, "r"); + if (fp) { + while(fgets(line, sizeof(line), fp)) { + backtrace_map_info_t* mi = parse_maps_line(line); + if (mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + return milist; +} + +#endif + +void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) { + while (milist) { + backtrace_map_info_t* next = milist->next; + free(milist); + milist = next; + } +} + +const backtrace_map_info_t* backtrace_find_map_info( + const backtrace_map_info_t* milist, uintptr_t addr) { + const backtrace_map_info_t* mi = milist; + while (mi && !(addr >= mi->start && addr < mi->end)) { + mi = mi->next; + } + return mi; +} diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c new file mode 100644 index 0000000..6f4cd3c --- /dev/null +++ b/libbacktrace/thread_utils.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 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 "thread_utils.h" + +#if defined(__APPLE__) + +#include <sys/syscall.h> + +// Mac OS >= 10.6 has a system call equivalent to Linux's gettid(). +pid_t gettid() { + return syscall(SYS_thread_selfid); +} + +#elif !defined(__BIONIC__) + +// glibc doesn't implement or export either gettid or tgkill. +#include <unistd.h> +#include <sys/syscall.h> + +pid_t gettid() { + return syscall(__NR_gettid); +} + +int tgkill(int tgid, int tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} + +#endif diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h new file mode 100644 index 0000000..ae4c929 --- /dev/null +++ b/libbacktrace/thread_utils.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBBACKTRACE_THREAD_UTILS_H +#define _LIBBACKTRACE_THREAD_UTILS_H + +#include <unistd.h> + +__BEGIN_DECLS + +int tgkill(int tgid, int tid, int sig); + +pid_t gettid(); + +__END_DECLS + +#endif /* _LIBBACKTRACE_THREAD_UTILS_H */ diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk index d62c2d5..e275317 100644 --- a/libcorkscrew/Android.mk +++ b/libcorkscrew/Android.mk @@ -51,7 +51,7 @@ endif LOCAL_SHARED_LIBRARIES += libdl libcutils liblog libgccdemangle -LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter LOCAL_MODULE := libcorkscrew LOCAL_MODULE_TAGS := optional @@ -81,7 +81,7 @@ ifeq ($(HOST_OS),linux) LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux? LOCAL_LDLIBS += -lrt endif -LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter LOCAL_MODULE := libcorkscrew LOCAL_MODULE_TAGS := optional include $(BUILD_HOST_SHARED_LIBRARY) diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c index e133ab6..ef22821 100755 --- a/libcorkscrew/arch-x86/backtrace-x86.c +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -380,7 +380,7 @@ static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie case DW_CFA_offset_extended: // probably we don't have it on x86. if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg > DWARF_REGISTERS) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } @@ -390,39 +390,39 @@ static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie break; case DW_CFA_restore_extended: // probably we don't have it on x86. if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - dstate->regs[reg].rule = stack->regs[reg].rule; - dstate->regs[reg].value = stack->regs[reg].value; - if (reg > DWARF_REGISTERS) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } + dstate->regs[reg].rule = stack->regs[reg].rule; + dstate->regs[reg].value = stack->regs[reg].value; ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value); break; case DW_CFA_undefined: // probably we don't have it on x86. if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - dstate->regs[reg].rule = 'u'; - dstate->regs[reg].value = 0; - if (reg > DWARF_REGISTERS) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } + dstate->regs[reg].rule = 'u'; + dstate->regs[reg].value = 0; ALOGV("DW_CFA_undefined: r%d", reg); break; case DW_CFA_same_value: // probably we don't have it on x86. if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; - dstate->regs[reg].rule = 's'; - dstate->regs[reg].value = 0; - if (reg > DWARF_REGISTERS) { + if (reg >= DWARF_REGISTERS) { ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); return false; } + dstate->regs[reg].rule = 's'; + dstate->regs[reg].value = 0; ALOGV("DW_CFA_same_value: r%d", reg); break; case DW_CFA_register: // probably we don't have it on x86. if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; /* that's new register actually, not offset */ if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; - if (reg > DWARF_REGISTERS || offset > DWARF_REGISTERS) { + if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) { ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS); return false; } @@ -520,7 +520,7 @@ static bool get_old_register_value(const memory_t* memory, uint32_t cfa, /* Updaing state based on dwarf state. */ static bool update_state(const memory_t* memory, unwind_state_t* state, - dwarf_state_t* dstate, cie_info_t* cie_info) { + dwarf_state_t* dstate) { unwind_state_t newstate; /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ /* Getting CFA. */ @@ -550,7 +550,6 @@ static bool update_state(const memory_t* memory, unwind_state_t* state, /* Execute CIE and FDE instructions for FDE found with find_fde. */ static bool execute_fde(const memory_t* memory, - const map_info_t* map_info_list, uintptr_t fde, unwind_state_t* state) { uint32_t fde_length = 0; @@ -753,7 +752,7 @@ static bool execute_fde(const memory_t* memory, ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc); } - return update_state(memory, state, dstate, cie_info); + return update_state(memory, state, dstate); } static ssize_t unwind_backtrace_common(const memory_t* memory, @@ -805,7 +804,7 @@ static ssize_t unwind_backtrace_common(const memory_t* memory, uint32_t stack_top = state->reg[DWARF_ESP]; - if (!execute_fde(memory, map_info_list, fde, state)) break; + if (!execute_fde(memory, fde, state)) break; if (frame) { frame->stack_top = stack_top; diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 0fd5a57..f8dda36 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -96,11 +96,6 @@ include $(BUILD_HOST_STATIC_LIBRARY) # Shared and static library for target # ======================================================== -# This is needed in LOCAL_C_INCLUDES to access the C library's private -# header named <bionic_time.h> -# -libcutils_c_includes := bionic/libc/private - include $(CLEAR_VARS) LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(commonSources) \ @@ -115,18 +110,18 @@ LOCAL_SRC_FILES := $(commonSources) \ uevent.c ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += arch-arm/memset32.S + LOCAL_SRC_FILES += arch-arm/memset32.S else # !arm -ifeq ($(TARGET_ARCH_VARIANT),x86-atom) -LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 -LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c -else # !x86-atom -ifeq ($(TARGET_ARCH),mips) -LOCAL_SRC_FILES += arch-mips/android_memset.c -else # !mips -LOCAL_SRC_FILES += memory.c -endif # !mips -endif # !x86-atom + ifeq ($(TARGET_ARCH),x86) + LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 + LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c + else # !x86 + ifeq ($(TARGET_ARCH),mips) + LOCAL_SRC_FILES += arch-mips/android_memset.c + else # !mips + LOCAL_SRC_FILES += memory.c + endif # !mips + endif # !x86 endif # !arm LOCAL_C_INCLUDES := $(libcutils_c_includes) $(KERNEL_HEADERS) diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S index b1f09cb..f8b79bd 100644 --- a/libcutils/arch-x86/android_memset16.S +++ b/libcutils/arch-x86/android_memset16.S @@ -17,16 +17,9 @@ * Contributed by: Intel Corporation */ -#if defined(USE_SSE2) - # include "cache_wrapper.S" # undef __i686 # define USE_AS_ANDROID # define sse2_memset16_atom android_memset16 # include "sse2-memset16-atom.S" -#else - -# include "memset16.S" - -#endif diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S index 1fb2ffe..6249fce 100644 --- a/libcutils/arch-x86/android_memset32.S +++ b/libcutils/arch-x86/android_memset32.S @@ -17,17 +17,9 @@ * Contributed by: Intel Corporation */ -#if defined(USE_SSE2) - # include "cache_wrapper.S" # undef __i686 # define USE_AS_ANDROID # define sse2_memset32_atom android_memset32 # include "sse2-memset32-atom.S" -#else - -# include "memset32.S" - -#endif - diff --git a/libcutils/arch-x86/sse2-memset16-atom.S b/libcutils/arch-x86/sse2-memset16-atom.S index cafec82..c2a762b 100644..100755 --- a/libcutils/arch-x86/sse2-memset16-atom.S +++ b/libcutils/arch-x86/sse2-memset16-atom.S @@ -86,7 +86,7 @@ name: \ # define SETRTNVAL movl DEST(%esp), %eax #endif -#ifdef SHARED +#if (defined SHARED || defined __PIC__) # define ENTRANCE PUSH (%ebx); # define RETURN_END POP (%ebx); ret # define RETURN RETURN_END; CFI_PUSH (%ebx) @@ -344,7 +344,7 @@ L(128bytesormore): PUSH (%ebx) mov $SHARED_CACHE_SIZE, %ebx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx @@ -362,7 +362,7 @@ L(128bytesormore): # define RESTORE_EBX_STATE CFI_PUSH (%ebx) cmp $DATA_CACHE_SIZE, %ecx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) # define RESTORE_EBX_STATE call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx @@ -471,7 +471,7 @@ L(128bytesormore_nt): jae L(128bytesormore_nt) sfence L(shared_cache_loop_end): -#if defined DATA_CACHE_SIZE || !defined SHARED +#if defined DATA_CACHE_SIZE || !(defined SHARED || defined __PIC__) POP (%ebx) #endif add %ecx, %edx diff --git a/libcutils/arch-x86/sse2-memset32-atom.S b/libcutils/arch-x86/sse2-memset32-atom.S index 4a52484..05eb64f 100644..100755 --- a/libcutils/arch-x86/sse2-memset32-atom.S +++ b/libcutils/arch-x86/sse2-memset32-atom.S @@ -86,7 +86,7 @@ name: \ # define SETRTNVAL #endif -#ifdef SHARED +#if (defined SHARED || defined __PIC__) # define ENTRANCE PUSH (%ebx); # define RETURN_END POP (%ebx); ret # define RETURN RETURN_END; CFI_PUSH (%ebx) @@ -259,7 +259,7 @@ L(128bytesormore): PUSH (%ebx) mov $SHARED_CACHE_SIZE, %ebx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx @@ -276,7 +276,7 @@ L(128bytesormore): # define RESTORE_EBX_STATE CFI_PUSH (%ebx) cmp $DATA_CACHE_SIZE, %ecx #else -# ifdef SHARED +# if (defined SHARED || defined __PIC__) # define RESTORE_EBX_STATE call __i686.get_pc_thunk.bx add $_GLOBAL_OFFSET_TABLE_, %ebx @@ -386,7 +386,7 @@ L(128bytesormore_nt): jae L(128bytesormore_nt) sfence L(shared_cache_loop_end): -#if defined DATA_CACHE_SIZE || !defined SHARED +#if defined DATA_CACHE_SIZE || !(defined SHARED || defined __PIC__) POP (%ebx) #endif add %ecx, %edx diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c index 8b71f87..3089a94 100644 --- a/libcutils/ashmem-dev.c +++ b/libcutils/ashmem-dev.c @@ -48,7 +48,7 @@ int ashmem_create_region(const char *name, size_t size) return fd; if (name) { - char buf[ASHMEM_NAME_LEN]; + char buf[ASHMEM_NAME_LEN] = {0}; strlcpy(buf, name, sizeof(buf)); ret = ioctl(fd, ASHMEM_SET_NAME, buf); diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c index be14af6..098b5db 100644 --- a/libcutils/dir_hash.c +++ b/libcutils/dir_hash.c @@ -159,6 +159,7 @@ static int recurse(HashAlgorithm algorithm, const char *directory_path, free(name); free(node); + closedir(d); return -1; } diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index 090d0e5..7906986 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -1,18 +1,18 @@ # Copyright 2008 The Android Open Source Project # LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c sha256.c +LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) - LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c sha256.c +LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c +LOCAL_CFLAGS := -Wall -Werror include $(BUILD_HOST_STATIC_LIBRARY) - include $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/test/Android.mk diff --git a/libmincrypt/dsa_sig.c b/libmincrypt/dsa_sig.c new file mode 100644 index 0000000..8df6cf7 --- /dev/null +++ b/libmincrypt/dsa_sig.c @@ -0,0 +1,125 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> + +#include "mincrypt/p256.h" + +/** + * Trims off the leading zero bytes and copy it to a buffer aligning it to the end. + */ +static inline int trim_to_p256_bytes(unsigned char dst[P256_NBYTES], unsigned char *src, + int src_len) { + int dst_offset; + while (*src == '\0' && src_len > 0) { + src++; + src_len--; + } + if (src_len > P256_NBYTES || src_len < 1) { + return 0; + } + dst_offset = P256_NBYTES - src_len; + memset(dst, 0, dst_offset); + memcpy(dst + dst_offset, src, src_len); + return 1; +} + +/** + * Unpacks the ASN.1 DSA signature sequence. + */ +int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int) { + /* + * Structure is: + * 0x30 0xNN SEQUENCE + s_length + * 0x02 0xNN INTEGER + r_length + * 0xAA 0xBB .. r_length bytes of "r" (offset 4) + * 0x02 0xNN INTEGER + s_length + * 0xMM 0xNN .. s_length bytes of "s" (offset 6 + r_len) + */ + int seq_len; + unsigned char r_bytes[P256_NBYTES]; + unsigned char s_bytes[P256_NBYTES]; + int r_len; + int s_len; + + memset(r_bytes, 0, sizeof(r_bytes)); + memset(s_bytes, 0, sizeof(s_bytes)); + + /* + * Must have at least: + * 2 bytes sequence header and length + * 2 bytes R integer header and length + * 1 byte of R + * 2 bytes S integer header and length + * 1 byte of S + * + * 8 bytes total + */ + if (sig_len < 8 || sig[0] != 0x30 || sig[2] != 0x02) { + return 0; + } + + seq_len = sig[1]; + if ((seq_len <= 0) || (seq_len + 2 != sig_len)) { + return 0; + } + + r_len = sig[3]; + /* + * Must have at least: + * 2 bytes for R header and length + * 2 bytes S integer header and length + * 1 byte of S + */ + if ((r_len < 1) || (r_len > seq_len - 5) || (sig[4 + r_len] != 0x02)) { + return 0; + } + s_len = sig[5 + r_len]; + + /** + * Must have: + * 2 bytes for R header and length + * r_len bytes for R + * 2 bytes S integer header and length + */ + if ((s_len < 1) || (s_len != seq_len - 4 - r_len)) { + return 0; + } + + /* + * ASN.1 encoded integers are zero-padded for positive integers. Make sure we have + * a correctly-sized buffer and that the resulting integer isn't too large. + */ + if (!trim_to_p256_bytes(r_bytes, &sig[4], r_len) + || !trim_to_p256_bytes(s_bytes, &sig[6 + r_len], s_len)) { + return 0; + } + + p256_from_bin(r_bytes, r_int); + p256_from_bin(s_bytes, s_int); + + return 1; +} diff --git a/libmincrypt/p256.c b/libmincrypt/p256.c new file mode 100644 index 0000000..1608d37 --- /dev/null +++ b/libmincrypt/p256.c @@ -0,0 +1,375 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This is an implementation of the P256 elliptic curve group. It's written to +// be portable 32-bit, although it's still constant-time. +// +// WARNING: Implementing these functions in a constant-time manner is far from +// obvious. Be careful when touching this code. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "mincrypt/p256.h" + +const p256_int SECP256r1_n = // curve order + {{0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1}}; + +const p256_int SECP256r1_p = // curve field size + {{-1, -1, -1, 0, 0, 0, 1, -1 }}; + +const p256_int SECP256r1_b = // curve b + {{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, + 0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}}; + +static const p256_int p256_one = P256_ONE; + +void p256_init(p256_int* a) { + memset(a, 0, sizeof(*a)); +} + +void p256_clear(p256_int* a) { p256_init(a); } + +int p256_get_bit(const p256_int* scalar, int bit) { + return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT) + >> (bit & (P256_BITSPERDIGIT - 1))) & 1; +} + +int p256_is_zero(const p256_int* a) { + int i, result = 0; + for (i = 0; i < P256_NDIGITS; ++i) result |= P256_DIGIT(a, i); + return !result; +} + +// top, c[] += a[] * b +// Returns new top +static p256_digit mulAdd(const p256_int* a, + p256_digit b, + p256_digit top, + p256_digit* c) { + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += *c; + carry += (p256_ddigit)P256_DIGIT(a, i) * b; + *c++ = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return top + (p256_digit)carry; +} + +// top, c[] -= top_a, a[] +static p256_digit subTop(p256_digit top_a, + const p256_digit* a, + p256_digit top_c, + p256_digit* c) { + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += *c; + borrow -= *a++; + *c++ = (p256_digit)borrow; + borrow >>= P256_BITSPERDIGIT; + } + borrow += top_c; + borrow -= top_a; + top_c = (p256_digit)borrow; + assert((borrow >> P256_BITSPERDIGIT) == 0); + return top_c; +} + +// top, c[] -= MOD[] & mask (0 or -1) +// returns new top. +static p256_digit subM(const p256_int* MOD, + p256_digit top, + p256_digit* c, + p256_digit mask) { + int i; + p256_sddigit borrow = 0; + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += *c; + borrow -= P256_DIGIT(MOD, i) & mask; + *c++ = (p256_digit)borrow; + borrow >>= P256_BITSPERDIGIT; + } + return top + (p256_digit)borrow; +} + +// top, c[] += MOD[] & mask (0 or -1) +// returns new top. +static p256_digit addM(const p256_int* MOD, + p256_digit top, + p256_digit* c, + p256_digit mask) { + int i; + p256_ddigit carry = 0; + for (i = 0; i < P256_NDIGITS; ++i) { + carry += *c; + carry += P256_DIGIT(MOD, i) & mask; + *c++ = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return top + (p256_digit)carry; +} + +// c = a * b mod MOD. c can be a and/or b. +void p256_modmul(const p256_int* MOD, + const p256_int* a, + const p256_digit top_b, + const p256_int* b, + p256_int* c) { + p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 }; + p256_digit top = 0; + int i; + + // Multiply/add into tmp. + for (i = 0; i < P256_NDIGITS; ++i) { + if (i) tmp[i + P256_NDIGITS - 1] = top; + top = mulAdd(a, P256_DIGIT(b, i), 0, tmp + i); + } + + // Multiply/add top digit + tmp[i + P256_NDIGITS - 1] = top; + top = mulAdd(a, top_b, 0, tmp + i); + + // Reduce tmp, digit by digit. + for (; i >= 0; --i) { + p256_digit reducer[P256_NDIGITS] = { 0 }; + p256_digit top_reducer; + + // top can be any value at this point. + // Guestimate reducer as top * MOD, since msw of MOD is -1. + top_reducer = mulAdd(MOD, top, 0, reducer); + + // Subtract reducer from top | tmp. + top = subTop(top_reducer, reducer, top, tmp + i); + + // top is now either 0 or 1. Make it 0, fixed-timing. + assert(top <= 1); + + top = subM(MOD, top, tmp + i, ~(top - 1)); + + assert(top == 0); + + // We have now reduced the top digit off tmp. Fetch new top digit. + top = tmp[i + P256_NDIGITS - 1]; + } + + // tmp might still be larger than MOD, yet same bit length. + // Make sure it is less, fixed-timing. + addM(MOD, 0, tmp, subM(MOD, 0, tmp, -1)); + + memcpy(c, tmp, P256_NBYTES); +} +int p256_is_odd(const p256_int* a) { return P256_DIGIT(a, 0) & 1; } +int p256_is_even(const p256_int* a) { return !(P256_DIGIT(a, 0) & 1); } + +p256_digit p256_shl(const p256_int* a, int n, p256_int* b) { + int i; + p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1); + + n %= P256_BITSPERDIGIT; + for (i = P256_NDIGITS - 1; i > 0; --i) { + p256_digit accu = (P256_DIGIT(a, i) << n); + accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n); + + top = (p256_digit)((((p256_ddigit)top) << n) >> P256_BITSPERDIGIT); + + return top; +} + +void p256_shr(const p256_int* a, int n, p256_int* b) { + int i; + + n %= P256_BITSPERDIGIT; + for (i = 0; i < P256_NDIGITS - 1; ++i) { + p256_digit accu = (P256_DIGIT(a, i) >> n); + accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n); +} + +static void p256_shr1(const p256_int* a, int highbit, p256_int* b) { + int i; + + for (i = 0; i < P256_NDIGITS - 1; ++i) { + p256_digit accu = (P256_DIGIT(a, i) >> 1); + accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1)); + P256_DIGIT(b, i) = accu; + } + P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) | + (highbit << (P256_BITSPERDIGIT - 1)); +} + +// Return -1, 0, 1 for a < b, a == b or a > b respectively. +int p256_cmp(const p256_int* a, const p256_int* b) { + int i; + p256_sddigit borrow = 0; + p256_digit notzero = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i); + // Track whether any result digit is ever not zero. + // Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) evaluating to 1. + notzero |= !!((p256_digit)borrow); + borrow >>= P256_BITSPERDIGIT; + } + return (int)borrow | notzero; +} + +// c = a - b. Returns borrow: 0 or -1. +int p256_sub(const p256_int* a, const p256_int* b, p256_int* c) { + int i; + p256_sddigit borrow = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i); + if (c) P256_DIGIT(c, i) = (p256_digit)borrow; + borrow >>= P256_BITSPERDIGIT; + } + return (int)borrow; +} + +// c = a + b. Returns carry: 0 or 1. +int p256_add(const p256_int* a, const p256_int* b, p256_int* c) { + int i; + p256_ddigit carry = 0; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += (p256_ddigit)P256_DIGIT(a, i) + P256_DIGIT(b, i); + if (c) P256_DIGIT(c, i) = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return (int)carry; +} + +// b = a + d. Returns carry, 0 or 1. +int p256_add_d(const p256_int* a, p256_digit d, p256_int* b) { + int i; + p256_ddigit carry = d; + + for (i = 0; i < P256_NDIGITS; ++i) { + carry += (p256_ddigit)P256_DIGIT(a, i); + if (b) P256_DIGIT(b, i) = (p256_digit)carry; + carry >>= P256_BITSPERDIGIT; + } + return (int)carry; +} + +// b = 1/a mod MOD, binary euclid. +void p256_modinv_vartime(const p256_int* MOD, + const p256_int* a, + p256_int* b) { + p256_int R = P256_ZERO; + p256_int S = P256_ONE; + p256_int U = *MOD; + p256_int V = *a; + + for (;;) { + if (p256_is_even(&U)) { + p256_shr1(&U, 0, &U); + if (p256_is_even(&R)) { + p256_shr1(&R, 0, &R); + } else { + // R = (R+MOD)/2 + p256_shr1(&R, p256_add(&R, MOD, &R), &R); + } + } else if (p256_is_even(&V)) { + p256_shr1(&V, 0, &V); + if (p256_is_even(&S)) { + p256_shr1(&S, 0, &S); + } else { + // S = (S+MOD)/2 + p256_shr1(&S, p256_add(&S, MOD, &S) , &S); + } + } else { // U,V both odd. + if (!p256_sub(&V, &U, NULL)) { + p256_sub(&V, &U, &V); + if (p256_sub(&S, &R, &S)) p256_add(&S, MOD, &S); + if (p256_is_zero(&V)) break; // done. + } else { + p256_sub(&U, &V, &U); + if (p256_sub(&R, &S, &R)) p256_add(&R, MOD, &R); + } + } + } + + p256_mod(MOD, &R, b); +} + +void p256_mod(const p256_int* MOD, + const p256_int* in, + p256_int* out) { + if (out != in) *out = *in; + addM(MOD, 0, P256_DIGITS(out), subM(MOD, 0, P256_DIGITS(out), -1)); +} + +// Verify y^2 == x^3 - 3x + b mod p +// and 0 < x < p and 0 < y < p +int p256_is_valid_point(const p256_int* x, const p256_int* y) { + p256_int y2, x3; + + if (p256_cmp(&SECP256r1_p, x) <= 0 || + p256_cmp(&SECP256r1_p, y) <= 0 || + p256_is_zero(x) || + p256_is_zero(y)) return 0; + + p256_modmul(&SECP256r1_p, y, 0, y, &y2); // y^2 + + p256_modmul(&SECP256r1_p, x, 0, x, &x3); // x^2 + p256_modmul(&SECP256r1_p, x, 0, &x3, &x3); // x^3 + if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - x + if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 2x + if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3); // x^3 - 3x + if (p256_add(&x3, &SECP256r1_b, &x3)) // x^3 - 3x + b + p256_sub(&x3, &SECP256r1_p, &x3); + + return p256_cmp(&y2, &x3) == 0; +} + +void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst) { + int i; + const uint8_t* p = &src[0]; + + for (i = P256_NDIGITS - 1; i >= 0; --i) { + P256_DIGIT(dst, i) = + (p[0] << 24) | + (p[1] << 16) | + (p[2] << 8) | + p[3]; + p += 4; + } +} diff --git a/libmincrypt/p256_ec.c b/libmincrypt/p256_ec.c new file mode 100644 index 0000000..90262cc --- /dev/null +++ b/libmincrypt/p256_ec.c @@ -0,0 +1,1279 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This is an implementation of the P256 elliptic curve group. It's written to +// be portable 32-bit, although it's still constant-time. +// +// WARNING: Implementing these functions in a constant-time manner is far from +// obvious. Be careful when touching this code. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +#include <stdint.h> +#include <stdio.h> + +#include <string.h> +#include <stdlib.h> + +#include "mincrypt/p256.h" + +typedef uint8_t u8; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; + +/* Our field elements are represented as nine 32-bit limbs. + * + * The value of an felem (field element) is: + * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228) + * + * That is, each limb is alternately 29 or 28-bits wide in little-endian + * order. + * + * This means that an felem hits 2**257, rather than 2**256 as we would like. A + * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems + * when multiplying as terms end up one bit short of a limb which would require + * much bit-shifting to correct. + * + * Finally, the values stored in an felem are in Montgomery form. So the value + * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257. + */ +typedef u32 limb; +#define NLIMBS 9 +typedef limb felem[NLIMBS]; + +static const limb kBottom28Bits = 0xfffffff; +static const limb kBottom29Bits = 0x1fffffff; + +/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and + * 28-bit words. */ +static const felem kOne = { + 2, 0, 0, 0xffff800, + 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, + 0 +}; +static const felem kZero = {0}; +static const felem kP = { + 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, + 0, 0, 0x200000, 0xf000000, + 0xfffffff +}; +static const felem k2P = { + 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, + 0, 0, 0x400000, 0xe000000, + 0x1fffffff +}; +/* kPrecomputed contains precomputed values to aid the calculation of scalar + * multiples of the base point, G. It's actually two, equal length, tables + * concatenated. + * + * The first table contains (x,y) felem pairs for 16 multiples of the base + * point, G. + * + * Index | Index (binary) | Value + * 0 | 0000 | 0G (all zeros, omitted) + * 1 | 0001 | G + * 2 | 0010 | 2**64G + * 3 | 0011 | 2**64G + G + * 4 | 0100 | 2**128G + * 5 | 0101 | 2**128G + G + * 6 | 0110 | 2**128G + 2**64G + * 7 | 0111 | 2**128G + 2**64G + G + * 8 | 1000 | 2**192G + * 9 | 1001 | 2**192G + G + * 10 | 1010 | 2**192G + 2**64G + * 11 | 1011 | 2**192G + 2**64G + G + * 12 | 1100 | 2**192G + 2**128G + * 13 | 1101 | 2**192G + 2**128G + G + * 14 | 1110 | 2**192G + 2**128G + 2**64G + * 15 | 1111 | 2**192G + 2**128G + 2**64G + G + * + * The second table follows the same style, but the terms are 2**32G, + * 2**96G, 2**160G, 2**224G. + * + * This is ~2KB of data. */ +static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = { + 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee, + 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3, + 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c, + 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22, + 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050, + 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b, + 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa, + 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2, + 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609, + 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581, + 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca, + 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33, + 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6, + 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd, + 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0, + 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881, + 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a, + 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26, + 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b, + 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023, + 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133, + 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa, + 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29, + 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc, + 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8, + 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59, + 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39, + 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689, + 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa, + 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3, + 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1, + 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f, + 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72, + 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d, + 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b, + 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a, + 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a, + 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f, + 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb, + 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc, + 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9, + 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce, + 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2, + 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca, + 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229, + 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57, + 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c, + 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa, + 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651, + 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec, + 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7, + 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c, + 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927, + 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298, + 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8, + 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2, + 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d, + 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4, + 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8, + 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78, +}; + + +/* Field element operations: */ + +/* NON_ZERO_TO_ALL_ONES returns: + * 0xffffffff for 0 < x <= 2**31 + * 0 for x == 0 or x > 2**31. + * + * x must be a u32 or an equivalent type such as limb. */ +#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1) + +/* felem_reduce_carry adds a multiple of p in order to cancel |carry|, + * which is a term at 2**257. + * + * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28. + * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */ +static void felem_reduce_carry(felem inout, limb carry) { + const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry); + + inout[0] += carry << 1; + inout[3] += 0x10000000 & carry_mask; + /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the + * previous line therefore this doesn't underflow. */ + inout[3] -= carry << 11; + inout[4] += (0x20000000 - 1) & carry_mask; + inout[5] += (0x10000000 - 1) & carry_mask; + inout[6] += (0x20000000 - 1) & carry_mask; + inout[6] -= carry << 22; + /* This may underflow if carry is non-zero but, if so, we'll fix it in the + * next line. */ + inout[7] -= 1 & carry_mask; + inout[7] += carry << 25; +} + +/* felem_sum sets out = in+in2. + * + * On entry, in[i]+in2[i] must not overflow a 32-bit word. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_sum(felem out, const felem in, const felem in2) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +#define two31m3 (((limb)1) << 31) - (((limb)1) << 3) +#define two30m2 (((limb)1) << 30) - (((limb)1) << 2) +#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2) +#define two31m2 (((limb)1) << 31) - (((limb)1) << 2) +#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2) +#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2) + +/* zero31 is 0 mod p. */ +static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2 }; + +/* felem_diff sets out = in-in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_diff(felem out, const felem in, const felem in2) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words + * with the same 29,28,... bit positions as an felem. + * + * The values in felems are in Montgomery form: x*R mod p where R = 2**257. + * Since we just multiplied two Montgomery values together, the result is + * x*y*R*R mod p. We wish to divide by R in order for the result also to be + * in Montgomery form. + * + * On entry: tmp[i] < 2**64 + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */ +static void felem_reduce_degree(felem out, u64 tmp[17]) { + /* The following table may be helpful when reading this code: + * + * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10... + * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29 + * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285 + * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */ + limb tmp2[18], carry, x, xMask; + unsigned i; + + /* tmp contains 64-bit words with the same 29,28,29-bit positions as an + * felem. So the top of an element of tmp might overlap with another + * element two positions down. The following loop eliminates this + * overlap. */ + tmp2[0] = (limb)(tmp[0] & kBottom29Bits); + + /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try + * and hint to the compiler that it can do a single-word shift by selecting + * the right register rather than doing a double-word shift and truncating + * afterwards. */ + tmp2[1] = ((limb) tmp[0]) >> 29; + tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits; + tmp2[1] += ((limb) tmp[1]) & kBottom28Bits; + carry = tmp2[1] >> 28; + tmp2[1] &= kBottom28Bits; + + for (i = 2; i < 17; i++) { + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 28; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom29Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 29; + tmp2[i] &= kBottom29Bits; + + i++; + if (i == 17) + break; + tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb)(tmp[i - 1])) >> 29; + tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom28Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 28; + tmp2[i] &= kBottom28Bits; + } + + tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25; + tmp2[17] += ((limb)(tmp[16])) >> 29; + tmp2[17] += (((limb)(tmp[16] >> 32)) << 3); + tmp2[17] += carry; + + /* Montgomery elimination of terms. + * + * Since R is 2**257, we can divide by R with a bitwise shift if we can + * ensure that the right-most 257 bits are all zero. We can make that true by + * adding multiplies of p without affecting the value. + * + * So we eliminate limbs from right to left. Since the bottom 29 bits of p + * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0. + * We can do that for 8 further limbs and then right shift to eliminate the + * extra factor of R. */ + for (i = 0;; i += 2) { + tmp2[i + 1] += tmp2[i] >> 29; + x = tmp2[i] & kBottom29Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i] = 0; + + /* The bounds calculations for this loop are tricky. Each iteration of + * the loop eliminates two words by adding values to words to their + * right. + * + * The following table contains the amounts added to each word (as an + * offset from the value of i at the top of the loop). The amounts are + * accounted for from the first and second half of the loop separately + * and are written as, for example, 28 to mean a value <2**28. + * + * Word: 3 4 5 6 7 8 9 10 + * Added in top half: 28 11 29 21 29 28 + * 28 29 + * 29 + * Added in bottom half: 29 10 28 21 28 28 + * 29 + * + * The value that is currently offset 7 will be offset 5 for the next + * iteration and then offset 3 for the iteration after that. Therefore + * the total value added will be the values added at 7, 5 and 3. + * + * The following table accumulates these values. The sums at the bottom + * are written as, for example, 29+28, to mean a value < 2**29+2**28. + * + * Word: 3 4 5 6 7 8 9 10 11 12 13 + * 28 11 10 29 21 29 28 28 28 28 28 + * 29 28 11 28 29 28 29 28 29 28 + * 29 28 21 21 29 21 29 21 + * 10 29 28 21 28 21 28 + * 28 29 28 29 28 29 28 + * 11 10 29 10 29 10 + * 29 28 11 28 11 + * 29 29 + * -------------------------------------------- + * 30+ 31+ 30+ 31+ 30+ + * 28+ 29+ 28+ 29+ 21+ + * 21+ 28+ 21+ 28+ 10 + * 10 21+ 10 21+ + * 11 11 + * + * So the greatest amount is added to tmp2[10] and tmp2[12]. If + * tmp2[10/12] has an initial value of <2**29, then the maximum value + * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32, + * as required. */ + tmp2[i + 3] += (x << 10) & kBottom28Bits; + tmp2[i + 4] += (x >> 18); + + tmp2[i + 6] += (x << 21) & kBottom29Bits; + tmp2[i + 7] += x >> 8; + + /* At position 200, which is the starting bit position for word 7, we + * have a factor of 0xf000000 = 2**28 - 2**24. */ + tmp2[i + 7] += 0x10000000 & xMask; + /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */ + tmp2[i + 8] += (x - 1) & xMask; + tmp2[i + 7] -= (x << 24) & kBottom28Bits; + tmp2[i + 8] -= x >> 4; + + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 8] -= x; + tmp2[i + 8] += (x << 28) & kBottom29Bits; + tmp2[i + 9] += ((x >> 1) - 1) & xMask; + + if (i+1 == NLIMBS) + break; + tmp2[i + 2] += tmp2[i + 1] >> 28; + x = tmp2[i + 1] & kBottom28Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i + 1] = 0; + + tmp2[i + 4] += (x << 11) & kBottom29Bits; + tmp2[i + 5] += (x >> 18); + + tmp2[i + 7] += (x << 21) & kBottom28Bits; + tmp2[i + 8] += x >> 7; + + /* At position 199, which is the starting bit of the 8th word when + * dealing with a context starting on an odd word, we have a factor of + * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th + * word from i+1 is i+8. */ + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 9] += (x - 1) & xMask; + tmp2[i + 8] -= (x << 25) & kBottom29Bits; + tmp2[i + 9] -= x >> 4; + + tmp2[i + 9] += 0x10000000 & xMask; + tmp2[i + 9] -= x; + tmp2[i + 10] += (x - 1) & xMask; + } + + /* We merge the right shift with a carry chain. The words above 2**257 have + * widths of 28,29,... which we need to correct when copying them down. */ + carry = 0; + for (i = 0; i < 8; i++) { + /* The maximum value of tmp2[i + 9] occurs on the first iteration and + * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is + * therefore safe. */ + out[i] = tmp2[i + 9]; + out[i] += carry; + out[i] += (tmp2[i + 10] << 28) & kBottom29Bits; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + out[i] = tmp2[i + 9] >> 1; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + out[8] = tmp2[17]; + out[8] += carry; + carry = out[8] >> 29; + out[8] &= kBottom29Bits; + + felem_reduce_carry(out, carry); +} + +/* felem_square sets out=in*in. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_square(felem out, const felem in) { + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in[0]; + tmp[1] = ((u64) in[0]) * (in[1] << 1); + tmp[2] = ((u64) in[0]) * (in[2] << 1) + + ((u64) in[1]) * (in[1] << 1); + tmp[3] = ((u64) in[0]) * (in[3] << 1) + + ((u64) in[1]) * (in[2] << 1); + tmp[4] = ((u64) in[0]) * (in[4] << 1) + + ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2]; + tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) * + (in[4] << 1) + ((u64) in[2]) * (in[3] << 1); + tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) * + (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) + + ((u64) in[3]) * (in[3] << 1); + tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) * + (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) + + ((u64) in[3]) * (in[4] << 1); + /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60, + * which is < 2**64 as required. */ + tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) * + (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) + + ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4]; + tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) * + (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) + + ((u64) in[4]) * (in[5] << 1); + tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) * + (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) + + ((u64) in[5]) * (in[5] << 1); + tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) * + (in[7] << 1) + ((u64) in[5]) * (in[6] << 1); + tmp[12] = ((u64) in[4]) * (in[8] << 1) + + ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6]; + tmp[13] = ((u64) in[5]) * (in[8] << 1) + + ((u64) in[6]) * (in[7] << 1); + tmp[14] = ((u64) in[6]) * (in[8] << 1) + + ((u64) in[7]) * (in[7] << 1); + tmp[15] = ((u64) in[7]) * (in[8] << 1); + tmp[16] = ((u64) in[8]) * in[8]; + + felem_reduce_degree(out, tmp); +} + +/* felem_mul sets out=in*in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_mul(felem out, const felem in, const felem in2) { + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in2[0]; + tmp[1] = ((u64) in[0]) * (in2[1] << 0) + + ((u64) in[1]) * (in2[0] << 0); + tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) * + (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0); + tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) * + (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) + + ((u64) in[3]) * (in2[0] << 0); + tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) * + (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) + + ((u64) in[3]) * (in2[1] << 1) + + ((u64) in[4]) * (in2[0] << 0); + tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) * + (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) + + ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) * + (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0); + tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) * + (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) + + ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) * + (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) + + ((u64) in[6]) * (in2[0] << 0); + tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) * + (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) + + ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) * + (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) + + ((u64) in[6]) * (in2[1] << 0) + + ((u64) in[7]) * (in2[0] << 0); + /* tmp[8] has the greatest value but doesn't overflow. See logic in + * felem_square. */ + tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) * + (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) + + ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) * + (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) + + ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) * + (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0); + tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) * + (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) + + ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) * + (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) + + ((u64) in[7]) * (in2[2] << 0) + + ((u64) in[8]) * (in2[1] << 0); + tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) * + (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) + + ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) * + (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) + + ((u64) in[8]) * (in2[2] << 0); + tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) * + (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) + + ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) * + (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0); + tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) * + (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) + + ((u64) in[7]) * (in2[5] << 1) + + ((u64) in[8]) * (in2[4] << 0); + tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) * + (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) + + ((u64) in[8]) * (in2[5] << 0); + tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) * + (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0); + tmp[15] = ((u64) in[7]) * (in2[8] << 0) + + ((u64) in[8]) * (in2[7] << 0); + tmp[16] = ((u64) in[8]) * (in2[8] << 0); + + felem_reduce_degree(out, tmp); +} + +static void felem_assign(felem out, const felem in) { + memcpy(out, in, sizeof(felem)); +} + +/* felem_inv calculates |out| = |in|^{-1} + * + * Based on Fermat's Little Theorem: + * a^p = a (mod p) + * a^{p-1} = 1 (mod p) + * a^{p-2} = a^{-1} (mod p) + */ +static void felem_inv(felem out, const felem in) { + felem ftmp, ftmp2; + /* each e_I will hold |in|^{2^I - 1} */ + felem e2, e4, e8, e16, e32, e64; + unsigned i; + + felem_square(ftmp, in); /* 2^1 */ + felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */ + felem_assign(e2, ftmp); + felem_square(ftmp, ftmp); /* 2^3 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^4 - 2^2 */ + felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */ + felem_assign(e4, ftmp); + felem_square(ftmp, ftmp); /* 2^5 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^6 - 2^2 */ + felem_square(ftmp, ftmp); /* 2^7 - 2^3 */ + felem_square(ftmp, ftmp); /* 2^8 - 2^4 */ + felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */ + felem_assign(e8, ftmp); + for (i = 0; i < 8; i++) { + felem_square(ftmp, ftmp); + } /* 2^16 - 2^8 */ + felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */ + felem_assign(e16, ftmp); + for (i = 0; i < 16; i++) { + felem_square(ftmp, ftmp); + } /* 2^32 - 2^16 */ + felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */ + felem_assign(e32, ftmp); + for (i = 0; i < 32; i++) { + felem_square(ftmp, ftmp); + } /* 2^64 - 2^32 */ + felem_assign(e64, ftmp); + felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */ + for (i = 0; i < 192; i++) { + felem_square(ftmp, ftmp); + } /* 2^256 - 2^224 + 2^192 */ + + felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */ + for (i = 0; i < 16; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^80 - 2^16 */ + felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */ + for (i = 0; i < 8; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^88 - 2^8 */ + felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */ + for (i = 0; i < 4; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^92 - 2^4 */ + felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */ + felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */ + felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */ + + felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */ +} + +/* felem_scalar_3 sets out=3*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_3(felem out) { + limb carry = 0; + unsigned i; + + for (i = 0;; i++) { + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_4 sets out=4*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_4(felem out) { + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 27; + out[i] <<= 2; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 26; + out[i] <<= 2; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_8 sets out=8*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */ +static void felem_scalar_8(felem out) { + limb carry = 0, next_carry; + unsigned i; + + for (i = 0;; i++) { + next_carry = out[i] >> 26; + out[i] <<= 3; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + next_carry = out[i] >> 25; + out[i] <<= 3; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of + * time depending on the value of |in|. */ +static char felem_is_zero_vartime(const felem in) { + limb carry; + int i; + limb tmp[NLIMBS]; + + felem_assign(tmp, in); + + /* First, reduce tmp to a minimal form. */ + do { + carry = 0; + for (i = 0;; i++) { + tmp[i] += carry; + carry = tmp[i] >> 29; + tmp[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + tmp[i] += carry; + carry = tmp[i] >> 28; + tmp[i] &= kBottom28Bits; + } + + felem_reduce_carry(tmp, carry); + } while (carry); + + /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */ + return memcmp(tmp, kZero, sizeof(tmp)) == 0 || + memcmp(tmp, kP, sizeof(tmp)) == 0 || + memcmp(tmp, k2P, sizeof(tmp)) == 0; +} + + +/* Group operations: + * + * Elements of the elliptic curve group are represented in Jacobian + * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in + * Jacobian form. */ + +/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */ +static void point_double(felem x_out, felem y_out, felem z_out, const felem x, + const felem y, const felem z) { + felem delta, gamma, alpha, beta, tmp, tmp2; + + felem_square(delta, z); + felem_square(gamma, y); + felem_mul(beta, x, gamma); + + felem_sum(tmp, x, delta); + felem_diff(tmp2, x, delta); + felem_mul(alpha, tmp, tmp2); + felem_scalar_3(alpha); + + felem_sum(tmp, y, z); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, gamma); + felem_diff(z_out, tmp, delta); + + felem_scalar_4(beta); + felem_square(x_out, alpha); + felem_diff(x_out, x_out, beta); + felem_diff(x_out, x_out, beta); + + felem_diff(tmp, beta, x_out); + felem_mul(tmp, alpha, tmp); + felem_square(tmp2, gamma); + felem_scalar_8(tmp2); + felem_diff(y_out, tmp, tmp2); +} + +/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}. + * (i.e. the second point is affine.) + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add_mixed(felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2) { + felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_sum(tmp, z1, z1); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, x1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, y1); + felem_sum(r, r, r); + felem_mul(v, x1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, y1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. */ +static void point_add(felem x_out, felem y_out, felem z_out, const felem x1, + const felem y1, const felem z1, const felem x2, + const felem y2, const felem z2) { + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} + + * {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */ +static void point_add_or_double_vartime( + felem x_out, felem y_out, felem z_out, const felem x1, const felem y1, + const felem z1, const felem x2, const felem y2, const felem z2) { + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + char x_equal, y_equal; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + x_equal = felem_is_zero_vartime(h); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + y_equal = felem_is_zero_vartime(r); + if (x_equal && y_equal) { + point_double(x_out, y_out, z_out, x1, y1, z1); + return; + } + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* copy_conditional sets out=in if mask = 0xffffffff in constant time. + * + * On entry: mask is either 0 or 0xffffffff. */ +static void copy_conditional(felem out, const felem in, limb mask) { + int i; + + for (i = 0; i < NLIMBS; i++) { + const limb tmp = mask & (in[i] ^ out[i]); + out[i] ^= tmp; + } +} + +/* select_affine_point sets {out_x,out_y} to the index'th entry of table. + * On entry: index < 16, table[0] must be zero. */ +static void select_affine_point(felem out_x, felem out_y, const limb* table, + limb index) { + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + } +} + +/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of + * table. On entry: index < 16, table[0] must be zero. */ +static void select_jacobian_point(felem out_x, felem out_y, felem out_z, + const limb* table, limb index) { + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + memset(out_z, 0, sizeof(felem)); + + /* The implicit value at index 0 is all zero. We don't need to perform that + * iteration of the loop because we already set out_* to zero. */ + table += 3 * NLIMBS; + + // Hit all entries to obscure cache profiling. + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_z[j] |= *table & mask; + } + } +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian + * number. Note that the value of scalar must be less than the order of the + * group. */ +static void scalar_base_mult(felem nx, felem ny, felem nz, + const p256_int* scalar) { + int i, j; + limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask; + u32 table_offset; + + felem px, py; + felem tx, ty, tz; + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + + /* The loop adds bits at positions 0, 64, 128 and 192, followed by + * positions 32,96,160 and 224 and does this 32 times. */ + for (i = 0; i < 32; i++) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + } + table_offset = 0; + for (j = 0; j <= 32; j += 32) { + char bit0 = p256_get_bit(scalar, 31 - i + j); + char bit1 = p256_get_bit(scalar, 95 - i + j); + char bit2 = p256_get_bit(scalar, 159 - i + j); + char bit3 = p256_get_bit(scalar, 223 - i + j); + limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3); + + select_affine_point(px, py, kPrecomputed + table_offset, index); + table_offset += 30 * NLIMBS; + + /* Since scalar is less than the order of the group, we know that + * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle + * below. */ + point_add_mixed(tx, ty, tz, nx, ny, nz, px, py); + /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero + * (a.k.a. the point at infinity). We handle that situation by + * copying the point from the table. */ + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, kOne, n_is_infinity_mask); + + /* Equally, the result is also wrong if the point from the table is + * zero, which happens when the index is zero. We handle that by + * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. */ + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + /* If p was not zero, then n is now non-zero. */ + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } + } +} + +/* point_to_affine converts a Jacobian point to an affine point. If the input + * is the point at infinity then it returns (0, 0) in constant time. */ +static void point_to_affine(felem x_out, felem y_out, const felem nx, + const felem ny, const felem nz) { + felem z_inv, z_inv_sq; + felem_inv(z_inv, nz); + felem_square(z_inv_sq, z_inv); + felem_mul(x_out, nx, z_inv_sq); + felem_mul(z_inv, z_inv, z_inv_sq); + felem_mul(y_out, ny, z_inv); +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */ +static void scalar_mult(felem nx, felem ny, felem nz, const felem x, + const felem y, const p256_int* scalar) { + int i; + felem px, py, pz, tx, ty, tz; + felem precomp[16][3]; + limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask; + + /* We precompute 0,1,2,... times {x,y}. */ + memset(precomp, 0, sizeof(felem) * 3); + memcpy(&precomp[1][0], x, sizeof(felem)); + memcpy(&precomp[1][1], y, sizeof(felem)); + memcpy(&precomp[1][2], kOne, sizeof(felem)); + + for (i = 2; i < 16; i += 2) { + point_double(precomp[i][0], precomp[i][1], precomp[i][2], + precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]); + + point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2], + precomp[i][0], precomp[i][1], precomp[i][2], x, y); + } + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + n_is_infinity_mask = -1; + + /* We add in a window of four bits each iteration and do this 64 times. */ + for (i = 0; i < 256; i += 4) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + } + + index = (p256_get_bit(scalar, 255 - i - 0) << 3) | + (p256_get_bit(scalar, 255 - i - 1) << 2) | + (p256_get_bit(scalar, 255 - i - 2) << 1) | + p256_get_bit(scalar, 255 - i - 3); + + /* See the comments in scalar_base_mult about handling infinities. */ + select_jacobian_point(px, py, pz, precomp[0][0], index); + point_add(tx, ty, tz, nx, ny, nz, px, py, pz); + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, pz, n_is_infinity_mask); + + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } +} + +#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} // 2^257 mod p256.p + +#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, 1, 0x7fffffff} // 1 / 2^257 mod p256.p + +static const p256_int kR = { kRDigits }; +static const p256_int kRInv = { kRInvDigits }; + +/* to_montgomery sets out = R*in. */ +static void to_montgomery(felem out, const p256_int* in) { + p256_int in_shifted; + int i; + + p256_init(&in_shifted); + p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted); + + for (i = 0; i < NLIMBS; i++) { + if ((i & 1) == 0) { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits; + p256_shr(&in_shifted, 29, &in_shifted); + } else { + out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits; + p256_shr(&in_shifted, 28, &in_shifted); + } + } + + p256_clear(&in_shifted); +} + +/* from_montgomery sets out=in/R. */ +static void from_montgomery(p256_int* out, const felem in) { + p256_int result, tmp; + int i, top; + + p256_init(&result); + p256_init(&tmp); + + p256_add_d(&tmp, in[NLIMBS - 1], &result); + for (i = NLIMBS - 2; i >= 0; i--) { + if ((i & 1) == 0) { + top = p256_shl(&result, 29, &tmp); + } else { + top = p256_shl(&result, 28, &tmp); + } + top |= p256_add_d(&tmp, in[i], &result); + } + + p256_modmul(&SECP256r1_p, &kRInv, top, &result, out); + + p256_clear(&result); + p256_clear(&tmp); +} + +/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the + * order of the group. */ +void p256_base_point_mul(const p256_int* n, p256_int* out_x, p256_int* out_y) { + felem x, y, z; + + scalar_base_mult(x, y, z, n); + + { + felem x_affine, y_affine; + + point_to_affine(x_affine, y_affine, x, y, z); + from_montgomery(out_x, x_affine); + from_montgomery(out_y, y_affine); + } +} + +/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where + * n1 and n2 are < the order of the group. + * + * As indicated by the name, this function operates in variable time. This + * is safe because it's used for signature validation which doesn't deal + * with secrets. */ +void p256_points_mul_vartime( + const p256_int* n1, const p256_int* n2, const p256_int* in_x, + const p256_int* in_y, p256_int* out_x, p256_int* out_y) { + felem x1, y1, z1, x2, y2, z2, px, py; + + /* If both scalars are zero, then the result is the point at infinity. */ + if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) { + p256_clear(out_x); + p256_clear(out_y); + return; + } + + to_montgomery(px, in_x); + to_montgomery(py, in_y); + scalar_base_mult(x1, y1, z1, n1); + scalar_mult(x2, y2, z2, px, py, n2); + + if (p256_is_zero(n2) != 0) { + /* If n2 == 0, then {x2,y2,z2} is zero and the result is just + * {x1,y1,z1}. */ + } else if (p256_is_zero(n1) != 0) { + /* If n1 == 0, then {x1,y1,z1} is zero and the result is just + * {x2,y2,z2}. */ + memcpy(x1, x2, sizeof(x2)); + memcpy(y1, y2, sizeof(y2)); + memcpy(z1, z2, sizeof(z2)); + } else { + /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */ + point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2); + } + + point_to_affine(px, py, x1, y1, z1); + from_montgomery(out_x, px); + from_montgomery(out_y, py); +} diff --git a/libmincrypt/p256_ecdsa.c b/libmincrypt/p256_ecdsa.c new file mode 100644 index 0000000..f2264b0 --- /dev/null +++ b/libmincrypt/p256_ecdsa.c @@ -0,0 +1,56 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> + +#include "mincrypt/p256_ecdsa.h" +#include "mincrypt/p256.h" + +int p256_ecdsa_verify(const p256_int* key_x, const p256_int* key_y, + const p256_int* message, + const p256_int* r, const p256_int* s) { + p256_int u, v; + + // Check public key. + if (!p256_is_valid_point(key_x, key_y)) return 0; + + // Check r and s are != 0 % n. + p256_mod(&SECP256r1_n, r, &u); + p256_mod(&SECP256r1_n, s, &v); + if (p256_is_zero(&u) || p256_is_zero(&v)) return 0; + + p256_modinv_vartime(&SECP256r1_n, s, &v); + p256_modmul(&SECP256r1_n, message, 0, &v, &u); // message / s % n + p256_modmul(&SECP256r1_n, r, 0, &v, &v); // r / s % n + + p256_points_mul_vartime(&u, &v, + key_x, key_y, + &u, &v); + + p256_mod(&SECP256r1_n, &u, &u); // (x coord % p) % n + return p256_cmp(r, &u) == 0; +} + diff --git a/libmincrypt/test/Android.mk b/libmincrypt/test/Android.mk index a28ccd8..73ff7d0 100644 --- a/libmincrypt/test/Android.mk +++ b/libmincrypt/test/Android.mk @@ -1,10 +1,15 @@ # Copyright 2013 The Android Open Source Project LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +include $(CLEAR_VARS) LOCAL_MODULE := rsa_test LOCAL_SRC_FILES := rsa_test.c LOCAL_STATIC_LIBRARIES := libmincrypt -include $(BUILD_HOST_EXECUTABLE) +include $(BUILD_HOST_NATIVE_TEST) +include $(CLEAR_VARS) +LOCAL_MODULE := ecdsa_test +LOCAL_SRC_FILES := ecdsa_test.c +LOCAL_STATIC_LIBRARIES := libmincrypt +include $(BUILD_HOST_NATIVE_TEST) diff --git a/libmincrypt/test/ecdsa_test.c b/libmincrypt/test/ecdsa_test.c new file mode 100644 index 0000000..b5a7b3a --- /dev/null +++ b/libmincrypt/test/ecdsa_test.c @@ -0,0 +1,278 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "mincrypt/p256.h" +#include "mincrypt/p256_ecdsa.h" +#include "mincrypt/sha256.h" + +/** + * Messages signed using: + * +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDw6UiziVMbjlfSpOAIpA2tcL+v1OlznZLnpadO8BGi1oAoGCCqGSM49 +AwEHoUQDQgAEZw7VAOjAXYRFuhZWYBgjahdOvkwcAnjGkxQWytZW+iS1hI3ZGE24 +6XmNka9IGxAgj2n/ip+MuZJMFoJ9DRea3g== +-----END EC PRIVATE KEY----- + */ + +p256_int key_x = { + .a = {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu, + 0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u} +}; +p256_int key_y = { + .a = {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au, + 0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u} +}; + +char* message_1 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_1 = + "30 44 02 20 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7" + "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66" + "7c 98 25 d9 02 20 54 f3 7f 5a e9 36 9c a2 f0 51" + "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7" + "ea 57 7e 88 46 12"; + +// Same as signature 1, but with leading zeroes. +char* message_2 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_2 = + "30 46 02 21 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7" + "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66" + "7c 98 25 d9 02 21 00 54 f3 7f 5a e9 36 9c a2 f0 51" + "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7" + "ea 57 7e 88 46 12"; + +// Excessive zeroes on the signature +char* message_3 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_3 = + "30 4c 02 24 00 00 00 00 43 18 fc eb 3b a8 3a a8 a3 cf 41 b7" + "81 4a f9 01 e1 8b 6e 95 c1 3a 83 25 9e a5 2e 66" + "7c 98 25 d9 02 24 00 00 00 00 54 f3 7f 5a e9 36 9c a2 f0 51" + "e0 6e 78 48 60 a3 f9 8a d5 2c 37 5a 0a 29 c9 f7" + "ea 57 7e 88 46 12"; + + +char* good_dsa_signature_1 = + "30 0D 02 01 01 02 08 00 A5 55 5A 01 FF A5 01"; +p256_int good_dsa_signature_1_r = { + .a = {0x00000001U, 0x00000000U, 0x00000000U, 0x00000000U, + 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U} +}; +p256_int good_dsa_signature_1_s = { + .a = {0x01FFA501U, 0x00A5555AU, 0x00000000U, 0x00000000U, + 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U} +}; + + +char* bad_dsa_signature_1 = + "a0 06 02 01 01 02 01 01"; + +char* bad_dsa_signature_2 = + "30 07 02 01 01 02 01 01"; + +char* bad_dsa_signature_3 = + "30 06 82 01 01 02 01 01"; + +char* bad_dsa_signature_4 = + "30 06 02 00 01 02 01 01"; + +char* bad_dsa_signature_5 = + "30 06 02 01 01 82 01 01"; + +char* bad_dsa_signature_6 = + "30 05 02 01 01 02 00"; + +char* bad_dsa_signature_7 = + "30 06 02 01 01 02 00 01"; + +unsigned char* parsehex(char* str, int* len) { + // result can't be longer than input + unsigned char* result = malloc(strlen(str)); + + unsigned char* p = result; + *len = 0; + + while (*str) { + int b; + + while (isspace(*str)) str++; + + switch (*str) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + b = (*str - '0') << 4; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + b = (*str - 'a' + 10) << 4; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + b = (*str - 'A' + 10) << 4; break; + case '\0': + return result; + default: + return NULL; + } + str++; + + while (isspace(*str)) str++; + + switch (*str) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + b |= *str - '0'; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + b |= *str - 'a' + 10; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + b |= *str - 'A' + 10; break; + default: + return NULL; + } + str++; + + *p++ = b; + ++*len; + } + + return result; +} + +int main(int arg, char** argv) { + + unsigned char hash_buf[SHA256_DIGEST_SIZE]; + + unsigned char* message; + int mlen; + unsigned char* signature; + int slen; + + p256_int hash; + p256_int r; + p256_int s; + + int success = 1; + +#define CHECK_DSA_SIG(sig, good) do {\ + message = parsehex(sig, &mlen); \ + int result = dsa_sig_unpack(message, mlen, &r, &s); \ + printf(#sig ": %s\n", result ? "good" : "bad"); \ + success = success && !(good ^ result); \ + free(message); \ + } while(0) +#define CHECK_GOOD_DSA_SIG(n) do {\ + CHECK_DSA_SIG(good_dsa_signature_##n, 1); \ + int result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_r), P256_DIGITS(&r), \ + P256_NBYTES); \ + success = success && result; \ + printf(" R value %s\n", result ? "good" : "bad"); \ + result = !memcmp(P256_DIGITS(&good_dsa_signature_##n##_s), P256_DIGITS(&s), \ + P256_NBYTES); \ + success = success && result; \ + printf(" S value %s\n", result ? "good" : "bad"); \ + } while (0) +#define CHECK_BAD_DSA_SIG(n) \ + CHECK_DSA_SIG(bad_dsa_signature_##n, 0) + + CHECK_GOOD_DSA_SIG(1); + + CHECK_BAD_DSA_SIG(1); + CHECK_BAD_DSA_SIG(2); + CHECK_BAD_DSA_SIG(3); + CHECK_BAD_DSA_SIG(4); + CHECK_BAD_DSA_SIG(5); + CHECK_BAD_DSA_SIG(6); + CHECK_BAD_DSA_SIG(7); + + +#define TEST_MESSAGE(n) do {\ + message = parsehex(message_##n, &mlen); \ + SHA256_hash(message, mlen, hash_buf); \ + p256_from_bin(hash_buf, &hash); \ + signature = parsehex(signature_##n, &slen); \ + int result = dsa_sig_unpack(signature, slen, &r, &s); \ + if (result) { result = p256_ecdsa_verify(&key_x, &key_y, &hash, &r, &s); } \ + printf("message %d: %s\n", n, result ? "verified" : "not verified"); \ + success = success && result; \ + free(signature); \ + } while(0) + + TEST_MESSAGE(1); + TEST_MESSAGE(2); + TEST_MESSAGE(3); + + printf("\n%s\n\n", success ? "PASS" : "FAIL"); + + return !success; +} diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk index b61234a..3154914 100644 --- a/libmincrypt/tools/Android.mk +++ b/libmincrypt/tools/Android.mk @@ -13,9 +13,10 @@ # limitations under the License. LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +include $(CLEAR_VARS) LOCAL_MODULE := dumpkey LOCAL_SRC_FILES := DumpPublicKey.java LOCAL_JAR_MANIFEST := DumpPublicKey.mf +LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index 7189116..3eb1398 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -16,6 +16,8 @@ package com.android.dumpkey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + import java.io.FileInputStream; import java.math.BigInteger; import java.security.cert.CertificateFactory; @@ -23,7 +25,10 @@ import java.security.cert.X509Certificate; import java.security.KeyStore; import java.security.Key; import java.security.PublicKey; +import java.security.Security; +import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECPoint; /** * Command line tool to extract RSA public keys from X.509 certificates @@ -39,9 +44,8 @@ class DumpPublicKey { * 3: 2048-bit RSA key with e=3 and SHA-256 hash * 4: 2048-bit RSA key with e=65537 and SHA-256 hash * @throws Exception if the key has the wrong size or public exponent - */ - static int check(RSAPublicKey key, boolean useSHA256) throws Exception { + static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); int version; @@ -64,12 +68,42 @@ class DumpPublicKey { } /** + * @param key to perform sanity checks on + * @return version number of key. Supported versions are: + * 5: 256-bit EC key with curve NIST P-256 + * @throws Exception if the key has the wrong size or public exponent + */ + static int checkEC(ECPublicKey key) throws Exception { + if (key.getParams().getCurve().getField().getFieldSize() != 256) { + throw new Exception("Curve must be NIST P-256"); + } + + return 5; + } + + /** + * Perform sanity check on public key. + */ + static int check(PublicKey key, boolean useSHA256) throws Exception { + if (key instanceof RSAPublicKey) { + return checkRSA((RSAPublicKey) key, useSHA256); + } else if (key instanceof ECPublicKey) { + if (!useSHA256) { + throw new Exception("Must use SHA-256 with EC keys!"); + } + return checkEC((ECPublicKey) key); + } else { + throw new Exception("Unsupported key class: " + key.getClass().getName()); + } + } + + /** * @param key to output * @return a String representing this public key. If the key is a * version 1 key, the string will be a C initializer; this is * not true for newer key versions. */ - static String print(RSAPublicKey key, boolean useSHA256) throws Exception { + static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception { int version = check(key, useSHA256); BigInteger N = key.getModulus(); @@ -128,11 +162,78 @@ class DumpPublicKey { return result.toString(); } + /** + * @param key to output + * @return a String representing this public key. If the key is a + * version 1 key, the string will be a C initializer; this is + * not true for newer key versions. + */ + static String printEC(ECPublicKey key) throws Exception { + int version = checkEC(key); + + StringBuilder result = new StringBuilder(); + + result.append("v"); + result.append(Integer.toString(version)); + result.append(" "); + + BigInteger X = key.getW().getAffineX(); + BigInteger Y = key.getW().getAffineY(); + int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8; // # of 32 bit integers in X coordinate + + result.append("{"); + result.append(nbytes); + + BigInteger B = BigInteger.valueOf(0x100L); // 2^8 + + // Write out Y coordinate as array of characters. + result.append(",{"); + for (int i = 0; i < nbytes; ++i) { + long n = X.mod(B).longValue(); + result.append(n); + + if (i != nbytes - 1) { + result.append(","); + } + + X = X.divide(B); + } + result.append("}"); + + // Write out Y coordinate as array of characters. + result.append(",{"); + for (int i = 0; i < nbytes; ++i) { + long n = Y.mod(B).longValue(); + result.append(n); + + if (i != nbytes - 1) { + result.append(","); + } + + Y = Y.divide(B); + } + result.append("}"); + + result.append("}"); + return result.toString(); + } + + static String print(PublicKey key, boolean useSHA256) throws Exception { + if (key instanceof RSAPublicKey) { + return printRSA((RSAPublicKey) key, useSHA256); + } else if (key instanceof ECPublicKey) { + return printEC((ECPublicKey) key); + } else { + throw new Exception("Unsupported key class: " + key.getClass().getName()); + } + } + public static void main(String[] args) { if (args.length < 1) { System.err.println("Usage: DumpPublicKey certfile ... > source.c"); System.exit(1); } + Security.addProvider(new BouncyCastleProvider()); try { for (int i = 0; i < args.length; i++) { FileInputStream input = new FileInputStream(args[i]); @@ -147,7 +248,7 @@ class DumpPublicKey { // anyway. Continue to do so for backwards // compatibility. useSHA256 = false; - } else if ("SHA256withRSA".equals(sigAlg)) { + } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) { useSHA256 = true; } else { System.err.println(args[i] + ": unsupported signature algorithm \"" + @@ -155,7 +256,7 @@ class DumpPublicKey { System.exit(1); } - RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey()); + PublicKey key = cert.getPublicKey(); check(key, useSHA256); System.out.print(print(key, useSHA256)); System.out.println(i < args.length - 1 ? "," : ""); diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c index dfb217b..3e72b57 100644 --- a/libsparse/backed_block.c +++ b/libsparse/backed_block.c @@ -370,7 +370,7 @@ int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb, } new_bb = malloc(sizeof(struct backed_block)); - if (bb == NULL) { + if (new_bb == NULL) { return -ENOMEM; } diff --git a/libsparse/output_file.c b/libsparse/output_file.c index 5014e4a..a28b0a5 100644 --- a/libsparse/output_file.c +++ b/libsparse/output_file.c @@ -46,15 +46,6 @@ #define off64_t off_t #endif -#ifdef __BIONIC__ -extern void* __mmap2(void *, size_t, int, int, int, off_t); -static inline void *mmap64(void *addr, size_t length, int prot, int flags, - int fd, off64_t offset) -{ - return __mmap2(addr, length, prot, flags, fd, offset >> 12); -} -#endif - #define min(a, b) \ ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) @@ -731,10 +722,12 @@ int write_fd_chunk(struct output_file *out, unsigned int len, } pos = lseek64(fd, offset, SEEK_SET); if (pos < 0) { + free(data); return -errno; } ret = read_all(fd, data, len); if (ret < 0) { + free(data); return ret; } ptr = data; diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h index 242ab35..9171d85 100644 --- a/mkbootimg/bootimg.h +++ b/mkbootimg/bootimg.h @@ -24,6 +24,7 @@ typedef struct boot_img_hdr boot_img_hdr; #define BOOT_MAGIC_SIZE 8 #define BOOT_NAME_SIZE 16 #define BOOT_ARGS_SIZE 512 +#define BOOT_EXTRA_ARGS_SIZE 1024 struct boot_img_hdr { @@ -43,10 +44,14 @@ struct boot_img_hdr unsigned unused[2]; /* future expansion: should be 0 */ unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ - + unsigned char cmdline[BOOT_ARGS_SIZE]; unsigned id[8]; /* timestamp / checksum / sha1 / etc */ + + /* Supplemental command line data; kept here to maintain + * binary compatibility with older versions of mkbootimg */ + unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; }; /* diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index 34a879b..d598f03 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -114,6 +114,7 @@ int main(int argc, char **argv) unsigned ramdisk_offset = 0x01000000; unsigned second_offset = 0x00f00000; unsigned tags_offset = 0x00000100; + size_t cmdlen; argc--; argv++; @@ -192,11 +193,19 @@ int main(int argc, char **argv) memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); - if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) { + cmdlen = strlen(cmdline); + if(cmdlen > (BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 2)) { fprintf(stderr,"error: kernel commandline too large\n"); return 1; } - strcpy((char*)hdr.cmdline, cmdline); + /* Even if we need to use the supplemental field, ensure we + * are still NULL-terminated */ + strncpy((char *)hdr.cmdline, cmdline, BOOT_ARGS_SIZE - 1); + hdr.cmdline[BOOT_ARGS_SIZE - 1] = '\0'; + if (cmdlen >= (BOOT_ARGS_SIZE - 1)) { + cmdline += (BOOT_ARGS_SIZE - 1); + strncpy((char *)hdr.extra_cmdline, cmdline, BOOT_EXTRA_ARGS_SIZE); + } kernel_data = load_file(kernel_fn, &hdr.kernel_size); if(kernel_data == 0) { diff --git a/rootdir/init.rc b/rootdir/init.rc index a0a9813..400c09e 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -17,6 +17,9 @@ on early-init # This should occur before anything else (e.g. ueventd) is started. setcon u:r:init:s0 + # Set the security context of /adb_keys if present. + restorecon /adb_keys + start ueventd # create mountpoints @@ -216,6 +219,10 @@ on post-fs-data mkdir /data/local 0751 root root mkdir /data/misc/media 0700 media media + # Set security context of any pre-existing /data/misc/adb/adb_keys file. + restorecon /data/misc/adb + restorecon /data/misc/adb/adb_keys + # For security reasons, /data/local/tmp should always be empty. # Do not place files or directories in /data/local/tmp mkdir /data/local/tmp 0771 shell shell @@ -253,6 +260,9 @@ on post-fs-data # Separate location for storing security policy files on data mkdir /data/security 0711 system system + # Reload policy from /data/security if present. + setprop selinux.reload_policy 1 + # If there is no fs-post-data action in the init.<device>.rc file, you # must uncomment this line, otherwise encrypted filesystems # won't work. @@ -347,7 +357,6 @@ on boot chown root radio /proc/cmdline # Set these so we can remotely update SELinux policy - chown system system /sys/fs/selinux/load chown system system /sys/fs/selinux/enforce # Define TCP buffer sizes for various networks @@ -417,10 +426,6 @@ service healthd-charger /sbin/healthd -n critical seclabel u:r:healthd:s0 -on property:selinux.reload_policy=1 - restart ueventd - restart installd - service console /system/bin/sh class core console diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 2cf0265..a60cfc5 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -5,6 +5,8 @@ /dev/tty 0666 root root /dev/random 0666 root root /dev/urandom 0666 root root +# Make HW RNG readable by group system to let EntropyMixer read it. +/dev/hw_random 0440 root system /dev/ashmem 0666 root root /dev/binder 0666 root root |