Fix null derefs in anal.avr plugin and improve defaults ##anal

This commit is contained in:
pancake 2022-05-01 21:33:21 +02:00 committed by pancake
parent cdd4748192
commit 0da98904c1
7 changed files with 159 additions and 150 deletions

View File

@ -15,8 +15,6 @@ https://en.wikipedia.org/wiki/Atmel_AVR_instruction_set
#include "../../asm/arch/avr/disasm.h"
static RDESContext desctx;
typedef struct _cpu_const_tag {
const char *const key;
ut8 type;
@ -36,6 +34,9 @@ typedef struct _cpu_model_tag {
CPU_CONST *consts[10];
} CPU_MODEL;
static R_TH_LOCAL RDESContext desctx;
static R_TH_LOCAL CPU_MODEL *Gcpu = NULL;
typedef void (*inst_handler_t) (RAnal *anal, RAnalOp *op, const ut8 *buf, int len, int *fail, CPU_MODEL *cpu);
typedef struct _opcodes_tag_ {
@ -58,7 +59,7 @@ static OPCODE_DESC* avr_op_analyze(RAnal *anal, RAnalOp *op, ut64 addr, const ut
}
#define MASK(bits) ((bits) == 32 ? 0xffffffff : (~((~((ut32) 0)) << (bits))))
#define CPU_PC_MASK(cpu) MASK((cpu)->pc)
#define CPU_PC_SIZE(cpu) ((((cpu)->pc) >> 3) + ((((cpu)->pc) & 0x07) ? 1 : 0))
#define CPU_PC_SIZE(cpu) cpu? ((((cpu)->pc) >> 3) + ((((cpu)->pc) & 0x07) ? 1 : 0)): 0
#define INST_HANDLER(OPCODE_NAME) static void _inst__ ## OPCODE_NAME (RAnal *anal, RAnalOp *op, const ut8 *buf, int len, int *fail, CPU_MODEL *cpu)
#define INST_DECL(OP, M, SL, C, SZ, T) { #OP, (M), (SL), _inst__ ## OP, (C), (SZ), R_ANAL_OP_TYPE_ ## T }
@ -143,7 +144,8 @@ CPU_MODEL cpu_models[] = {
// CPU_MODEL_DECL ("ATmega168", 13, 512, 512),
// last model is the default AVR - ATmega8 forever!
{
.model = "ATmega8", .pc = 13,
.model = "ATmega8",
.pc = 13,
.consts = {
cpu_reg_common,
cpu_memsize_common,
@ -159,34 +161,31 @@ static CPU_MODEL *get_cpu_model(const char *model);
static CPU_MODEL *__get_cpu_model_recursive(const char *model) {
CPU_MODEL *cpu = NULL;
for (cpu = cpu_models; cpu < cpu_models + ((sizeof (cpu_models) / sizeof (CPU_MODEL))) - 1; cpu++) {
if (!r_str_casecmp (model, cpu->model)) {
break;
}
}
// fix inheritance tree
if (cpu->inherit && !cpu->inherit_cpu_p) {
if (cpu && cpu->inherit && !cpu->inherit_cpu_p) {
cpu->inherit_cpu_p = get_cpu_model (cpu->inherit);
if (!cpu->inherit_cpu_p) {
eprintf ("ERROR: Cannot inherit from unknown CPU model '%s'.\n", cpu->inherit);
R_LOG_ERROR ("Cannot inherit from unknown CPU model '%s'.", cpu->inherit);
}
}
return cpu;
}
static CPU_MODEL *get_cpu_model(const char *model) {
static CPU_MODEL *cpu = NULL;
if (!model) {
return NULL;
model = "ATmega8";
}
// cache
if (cpu && cpu->model && !r_str_casecmp (model, cpu->model)) {
return cpu;
if (Gcpu && Gcpu->model && !r_str_casecmp (model, Gcpu->model)) {
return Gcpu;
}
return cpu = __get_cpu_model_recursive (model);
Gcpu = __get_cpu_model_recursive (model);
return Gcpu;
}
static ut32 const_get_value(CPU_CONST *c) {
@ -195,11 +194,13 @@ static ut32 const_get_value(CPU_CONST *c) {
static CPU_CONST *const_by_name(CPU_MODEL *cpu, int type, char *c) {
CPU_CONST **clist, *citem;
if (!cpu) {
return NULL;
}
for (clist = cpu->consts; *clist; clist++) {
for (citem = *clist; citem->key; citem++) {
if (!strcmp (c, citem->key)
&& (type == CPU_CONST_NONE || type == citem->type)) {
if (!strcmp (c, citem->key) && (type == CPU_CONST_NONE || type == citem->type)) {
return citem;
}
}
@ -207,7 +208,7 @@ static CPU_CONST *const_by_name(CPU_MODEL *cpu, int type, char *c) {
if (cpu->inherit_cpu_p) {
return const_by_name (cpu->inherit_cpu_p, type, c);
}
eprintf ("ERROR: CONSTANT key[%s] NOT FOUND.\n", c);
R_LOG_ERROR ("CONSTANT key[%s] NOT FOUND.", c);
return NULL;
}
@ -482,14 +483,18 @@ INST_HANDLER (call) { // CALL k
| (buf[0] & 0x01) << 17
| (buf[0] & 0xf0) << 14;
op->fail = op->addr + op->size;
op->cycles = cpu->pc <= 16 ? 3 : 4;
if (!STR_BEGINS (cpu->model, "ATxmega")) {
op->cycles--; // AT*mega optimizes one cycle
if (cpu) {
op->cycles = cpu->pc <= 16 ? 3 : 4;
if (!STR_BEGINS (cpu->model, "ATxmega")) {
op->cycles--; // AT*mega optimizes one cycle
}
ESIL_A ("pc,"); // esil is already pointing to
// next instruction (@ret)
__generic_push (op, CPU_PC_SIZE (cpu)); // push @ret in stack
ESIL_A ("%"PFMT64d",pc,=,", op->jump); // jump!
} else {
op->cycles = 1;
}
ESIL_A ("pc,"); // esil is already pointing to
// next instruction (@ret)
__generic_push (op, CPU_PC_SIZE (cpu)); // push @ret in stack
ESIL_A ("%"PFMT64d",pc,=,", op->jump); // jump!
}
INST_HANDLER (cbi) { // CBI A, b
@ -1115,23 +1120,20 @@ INST_HANDLER (pop) { // POP Rd
int d = ((buf[1] & 0x1) << 4) | ((buf[0] >> 4) & 0xf);
__generic_pop (op, 1);
ESIL_A ("r%d,=,", d); // store in Rd
}
INST_HANDLER (push) { // PUSH Rr
INST_HANDLER(push) { // PUSH Rr
if (len < 2) {
return;
}
int r = ((buf[1] & 0x1) << 4) | ((buf[0] >> 4) & 0xf);
ESIL_A ("r%d,", r); // load Rr
__generic_push (op, 1); // push it into stack
// cycles
op->cycles = !STR_BEGINS (cpu->model, "ATxmega")
? 1 // AT*mega optimizes one cycle
: 2;
ESIL_A ("r%d,", r); // load Rr
__generic_push (op, 1); // push it into stack
// AT*mega optimizes one cycle
op->cycles = !STR_BEGINS (cpu->model, "ATxmega") ?1: 2;
}
INST_HANDLER (rcall) { // RCALL k
INST_HANDLER(rcall) { // RCALL k
if (len < 2) {
return;
}
@ -1697,8 +1699,7 @@ static int avr_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *buf, int len,
int size = avr_anal (anal, mnemonic, sizeof (mnemonic), addr, buf, len);
if (!strcmp (mnemonic, "invalid")
|| !strcmp (mnemonic, "truncated")) {
if (!strcmp (mnemonic, "invalid") || !strcmp (mnemonic, "truncated")) {
op->eob = true;
op->mnemonic = strdup (mnemonic);
op->size = 2;
@ -1713,7 +1714,7 @@ static int avr_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *buf, int len,
ut64 offset = 0;
r_anal_esil_reg_write (anal->esil, "_prog", offset);
offset += (1ULL << cpu->pc);
offset += (1ULL << (cpu ? cpu->pc: 8));
r_anal_esil_reg_write (anal->esil, "_io", offset);
offset += const_get_value (const_by_name (cpu, CPU_CONST_PARAM, "sram_start"));
@ -2357,12 +2358,26 @@ RAnalPlugin r_anal_plugin_avr = {
.arch = "avr",
.esil = true,
.archinfo = archinfo,
.endian = R_SYS_ENDIAN_LITTLE | R_SYS_ENDIAN_BIG,
.bits = 8 | 16, // 24 big regs conflicts
.op = &avr_op,
.set_reg_profile = &set_reg_profile,
.esil_init = esil_avr_init,
.esil_fini = esil_avr_fini,
.anal_mask = anal_mask_avr
.anal_mask = anal_mask_avr,
.cpus =
"ATmega8," // First one is default
"ATmega1280,"
"ATmega1281,"
"ATmega168,"
"ATmega2560,"
"ATmega2561,"
"ATmega328p,"
"ATmega32u4,"
"ATmega48,"
"ATmega640,"
"ATmega88,"
"ATxmega128a4u"
};
#ifndef R2_PLUGIN_INCORE

View File

@ -126,7 +126,7 @@ static int archinfo(RAnal *anal, int q) {
return 2;
}
RAnalPlugin r_anal_plugin_s390_gnu= {
RAnalPlugin r_anal_plugin_s390_gnu = {
.name = "s390.gnu",
.desc = "SystemZ S390 from binutils",
.esil = false,

View File

@ -38,106 +38,6 @@
static int disassembleOperands(disassembledInstruction *dInstruction);
/* Extracts certain bits of data from a mask, used to extract operands from their encoding in the opcode. */
static uint16_t extractDataFromMask(uint16_t data, uint16_t mask);
/* Look up an instruction by it's opcode in the instructionSet,
* starting from index offset. Always returns a valid instruction
* index because the last instruction in the instruction set database
* is set to be a generic data word (.DW). */
static int lookupInstruction(uint16_t opcode, int offset);
/* Disassembles an assembled instruction, including its operands. */
int disassembleInstruction(avrDisassembleContext *context, disassembledInstruction *dInstruction, const assembledInstruction aInstruction) {
int insidx, i;
if (!dInstruction || !context)
return ERROR_INVALID_ARGUMENTS;
/* Look up the instruction */
insidx = lookupInstruction(aInstruction.opcode, 0);
if (insidx == AVR_TOTAL_INSTRUCTIONS) {
// invalid instruction
return 0;
}
/*** AVR SPECIFIC */
/* If a long instruction was found in the last instruction disassembly,
* extract the rest of the address, and indicate that it is to be printed */
if (context->status == AVR_LONG_INSTRUCTION_FOUND) {
context->status = AVR_LONG_INSTRUCTION_PRINT;
context->longAddress |= aInstruction.opcode;
/* We must multiply by two, because every instruction is 2 bytes,
* so in order to jump/call to the right address (which increments by
* two for every instruction), we must multiply this distance by two. */
//printf ("ii=%d\n", insidx);
if(!strcmp(context->longInstruction.instruction->mnemonic, "call")||
!strcmp(context->longInstruction.instruction->mnemonic, "jmp")) {
context->longAddress *= 2;
}
*dInstruction = context->longInstruction;
return 0;
/* If a long instruction was printed in the last instruction disassembly,
* reset the AVR_Call_Instruction variable back to zero. */
} else if (context->status == AVR_LONG_INSTRUCTION_PRINT) {
context->status = 0;
}
/* Copy over the address, and reference to the instruction, set
* the equivilant-encoded but different instruction to NULL for now. */
dInstruction->address = aInstruction.address;
dInstruction->instruction = &instructionSet[insidx];
dInstruction->alternateInstruction = NULL;
/* Copy out each operand, extracting the operand data from the original
* opcode using the operand mask. */
for (i = 0; i < instructionSet[insidx].numOperands; i++) {
dInstruction->operands[i] = extractDataFromMask(aInstruction.opcode, dInstruction->instruction->operandMasks[i]);
/*** AVR SPECIFIC */
/* If this is an instruction with a long absolute operand, indicate that a long instruction has been found,
* and extract the first part of the long address. */
if (dInstruction->instruction->operandTypes[i] == OPERAND_LONG_ABSOLUTE_ADDRESS) {
context->status = AVR_LONG_INSTRUCTION_FOUND;
context->longAddress = dInstruction->operands[i] << 16;
context->longInstruction = *dInstruction;
}
}
/* Disassemble operands */
if (disassembleOperands(dInstruction) < 0)
return ERROR_INVALID_ARGUMENTS; /* Only possible error for disassembleOperands() */
if (context->status == AVR_LONG_INSTRUCTION_FOUND) {
/* If we found a long instruction (32-bit one),
* Copy this instruction over to our special longInstruction variable, that
* will exist even after we move onto the next 16-bits */
context->longInstruction = *dInstruction;
}
return 0;
}
/* Extracts certain bits of data from a mask, used to extract operands from their encoding in the opcode. */
static uint16_t extractDataFromMask(uint16_t data, uint16_t mask) {
int i, j;
uint16_t result = 0;
/* i counts through every bit of the data,
* j counts through every bit of the data we're copying out. */
for (i = 0, j = 0; i < 16; i++) {
/* If the mask has a bit in this position */
if (mask & (1<<i)) {
/* If there is a data bit with this mask bit,
* then toggle that bit in the extracted data (result).
* Notice that it uses its own bit counter j. */
if (((mask & (1<<i)) & data) != 0)
result |= (1<<j);
/* Increment the extracted data bit count. */
j++;
}
}
return result;
}
/* Look up an instruction by it's opcode in the instructionSet,
* starting from index offset. Always returns a valid instruction
@ -173,11 +73,12 @@ static int lookupInstruction(uint16_t opcode, int offset) {
/* If we encountered a ghost register and were unable confirm that
* all register operands were equal (in this case ghostRegisterConfirmed
* would have changed), then move the match-search onto the next instruction. */
if (ghostRegisterConfirmed == 0)
if (ghostRegisterConfirmed == 0) {
continue;
if (opcodeSearch == instructionSet[insidx].opcodeMask)
}
if (opcodeSearch == instructionSet[insidx].opcodeMask) {
break;
}
}
/* It's impossible not to find an instruction, because the last instruction ".DW",
* specifies a word of data at the addresses, instead of an instruction.
@ -186,16 +87,108 @@ static int lookupInstruction(uint16_t opcode, int offset) {
return insidx;
}
/* Disassembles an assembled instruction, including its operands. */
int disassembleInstruction(avrDisassembleContext *context, disassembledInstruction *dInstruction, const assembledInstruction aInstruction) {
int insidx, i;
if (!dInstruction || !context) {
return ERROR_INVALID_ARGUMENTS;
}
/* Look up the instruction */
insidx = lookupInstruction (aInstruction.opcode, 0);
if (insidx == AVR_TOTAL_INSTRUCTIONS) {
// invalid instruction
return 0;
}
/*** AVR SPECIFIC */
/* If a long instruction was found in the last instruction disassembly,
* extract the rest of the address, and indicate that it is to be printed */
if (context->status == AVR_LONG_INSTRUCTION_FOUND) {
context->status = AVR_LONG_INSTRUCTION_PRINT;
context->longAddress |= aInstruction.opcode;
/* We must multiply by two, because every instruction is 2 bytes,
* so in order to jump/call to the right address (which increments by
* two for every instruction), we must multiply this distance by two. */
//printf ("ii=%d\n", insidx);
if(!strcmp(context->longInstruction.instruction->mnemonic, "call") ||
!strcmp(context->longInstruction.instruction->mnemonic, "jmp")) {
context->longAddress *= 2;
}
*dInstruction = context->longInstruction;
return 0;
/* If a long instruction was printed in the last instruction disassembly,
* reset the AVR_Call_Instruction variable back to zero. */
} else if (context->status == AVR_LONG_INSTRUCTION_PRINT) {
context->status = 0;
}
/* Copy over the address, and reference to the instruction, set
* the equivilant-encoded but different instruction to NULL for now. */
dInstruction->address = aInstruction.address;
dInstruction->instruction = &instructionSet[insidx];
dInstruction->alternateInstruction = NULL;
/* Copy out each operand, extracting the operand data from the original
* opcode using the operand mask. */
for (i = 0; i < instructionSet[insidx].numOperands; i++) {
dInstruction->operands[i] = extractDataFromMask(aInstruction.opcode, dInstruction->instruction->operandMasks[i]);
/*** AVR SPECIFIC */
/* If this is an instruction with a long absolute operand, indicate that a long instruction has been found,
* and extract the first part of the long address. */
if (dInstruction->instruction->operandTypes[i] == OPERAND_LONG_ABSOLUTE_ADDRESS) {
context->status = AVR_LONG_INSTRUCTION_FOUND;
context->longAddress = dInstruction->operands[i] << 16;
context->longInstruction = *dInstruction;
}
}
/* Disassemble operands */
if (disassembleOperands(dInstruction) < 0) {
return ERROR_INVALID_ARGUMENTS; /* Only possible error for disassembleOperands() */
}
if (context->status == AVR_LONG_INSTRUCTION_FOUND) {
/* If we found a long instruction (32-bit one),
* Copy this instruction over to our special longInstruction variable, that
* will exist even after we move onto the next 16-bits */
context->longInstruction = *dInstruction;
}
return 0;
}
/* Extracts certain bits of data from a mask, used to extract operands from their encoding in the opcode. */
static uint16_t extractDataFromMask(uint16_t data, uint16_t mask) {
int i, j;
uint16_t result = 0;
/* i counts through every bit of the data,
* j counts through every bit of the data we're copying out. */
for (i = 0, j = 0; i < 16; i++) {
/* If the mask has a bit in this position */
if (mask & (1<<i)) {
/* If there is a data bit with this mask bit,
* then toggle that bit in the extracted data (result).
* Notice that it uses its own bit counter j. */
if (((mask & (1<<i)) & data) != 0)
result |= (1<<j);
/* Increment the extracted data bit count. */
j++;
}
}
return result;
}
/* Disassembles/decodes operands back to their original form. */
static int disassembleOperands(disassembledInstruction *dInstruction) {
int i;
/* This should never happen */
if (!dInstruction)
if (!dInstruction || !dInstruction->instruction) {
return ERROR_INVALID_ARGUMENTS;
if (!dInstruction->instruction)
return ERROR_INVALID_ARGUMENTS;
}
/* For each operand, decode its original value. */
for (i = 0; i < dInstruction->instruction->numOperands; i++) {
switch (dInstruction->instruction->operandTypes[i]) {

View File

@ -45,7 +45,7 @@ RAsmPlugin r_asm_plugin_avr = {
.name = "avr",
.arch = "avr",
.license = "GPL",
.bits = 8|16,
.bits = 8 | 16,
.endian = R_SYS_ENDIAN_LITTLE | R_SYS_ENDIAN_BIG,
.desc = "AVR Atmel",
.disassemble = &disassemble,

View File

@ -1327,6 +1327,7 @@ typedef struct r_anal_plugin_t {
char *arch;
char *author;
char *version;
int endian; // bitmask to define little, big, etc.
char *cpus;
int bits;
int esil; // can do esil or not

View File

@ -45,7 +45,7 @@ static inline void my_ac_free(RArchConfig *cfg) {
static inline void r_arch_use(RArchConfig *config, R_NULLABLE const char *arch) {
r_return_if_fail (config);
R_LOG_DEBUG ("RArch.USE (%s)", arch);
// R_LOG_DEBUG ("RArch.USE (%s)", arch);
if (arch && !strcmp (arch, "null")) {
return;
}
@ -55,7 +55,7 @@ static inline void r_arch_use(RArchConfig *config, R_NULLABLE const char *arch)
static inline void r_arch_set_cpu(RArchConfig *config, R_NULLABLE const char *cpu) {
r_return_if_fail (config);
R_LOG_DEBUG ("RArch.CPU (%s)", cpu);
// R_LOG_DEBUG ("RArch.CPU (%s)", cpu);
free (config->cpu);
config->cpu = R_STR_ISNOTEMPTY (cpu) ? strdup (cpu) : NULL;
}

View File

@ -3,7 +3,7 @@ FILE=malloc://1024
CMDS=<<EOF
e asm.arch=avr
e asm.bits=8
wx 0xf8 0x94 0x08 0xe0 0x0e 0xbf 0x0f 0xe5 0x0d 0xbf 0x0e 0x94 0x08 0x00 0x88 0x95 0x08 0x95
wx f89408e00ebf0fe50dbf0e94080088950895
aei
aeim 0x00000100 0xffff avr_ram
6aeso
@ -16,4 +16,4 @@ EXPECT=<<EOF
0x0000000e
0x0000000e
EOF
RUN
RUN