### BEGIN LICENSE ###
### Use of the triage tools and related source code is subject to the terms
### of the license below.
###
### ------------------------------------------------------------------------
### Copyright (C) 2011 Carnegie Mellon University. All Rights Reserved.
### Portions Copyright 2013 BlackBerry Ltd. All Rights Reserved.
### ------------------------------------------------------------------------
### Redistribution and use in source and binary forms, with or without
### modification, are permitted provided that the following conditions are
### met:
###
### 1. Redistributions of source code must retain the above copyright
###    notice, this list of conditions and the following acknowledgments
###    and disclaimers.
###
### 2. 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.
###
### 3. All advertising materials for third-party software mentioning
###    features or use of this software must display the following
###    disclaimer:
###
###    "Neither Carnegie Mellon University nor its Software Engineering
###     Institute have reviewed or endorsed this software"
###
### 4. The names "Department of Homeland Security," "Carnegie Mellon
###    University," "CERT" and/or "Software Engineering Institute" shall
###    not be used to endorse or promote products derived from this software
###    without prior written permission. For written permission, please
###    contact permission@sei.cmu.edu.
###
### 5. Products derived from this software may not be called "CERT" nor
###    may "CERT" appear in their names without prior written permission of
###    permission@sei.cmu.edu.
###
### 6. Redistributions of any form whatsoever must retain the following
###    acknowledgment:
###
###    "This product includes software developed by CERT with funding
###     and support from the Department of Homeland Security under
###     Contract No. FA 8721-05-C-0003."
###
### THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
### CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
### EXPRESS OR IMPLIED, AS TO ANY MATTER, AND ALL SUCH WARRANTIES, INCLUDING
### WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE
### EXPRESSLY DISCLAIMED. WITHOUT LIMITING THE GENERALITY OF THE FOREGOING,
### CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND
### RELATING TO EXCLUSIVITY, INFORMATIONAL CONTENT, ERROR-FREE OPERATION,
### RESULTS TO BE OBTAINED FROM USE, FREEDOM FROM PATENT, TRADEMARK AND
### COPYRIGHT INFRINGEMENT AND/OR FREEDOM FROM THEFT OF TRADE SECRETS.
### END LICENSE ###
'''
Contains analyzers used to match rules that are used to classify the state
of an ARM Linux GDB inferior and some helper functions.

NOTE: This logic was contributed by a user that was testing on ARM. As of
17 Jan 2014, continuous integration only ensures that results of analysis
on ARM are consistent, not correct. In other words, please help fix this code!
'''

import re
import signal

from lib.tools import memoized
from lib.analyzers.x86 import Analyzer

class ArmAnalyzer(Analyzer):
    '''
    An analyzer for an ARM Linux GDB target. See x86.py for more info on 
    Analyzers.
    '''

    def __init__(self, target):
        Analyzer.__init__(self, target)

    # EXPLOITABLE matching methods follow

    @memoized
    def isReturnAv(self):
        # not as easy, 'ret' is one of many ways
        ins = self.target.current_instruction()
        return self.isAccessViolationSignal() and ins and \
            ((self.isJumpInstruction() and "lr" in ins.source.regs) or \
             (ins.source.is_pointer and "pc" in ins.dest.regs))

    @memoized
    def isHeapError(self):
        libc_bts = [ ["abort", "__libc_message", "malloc_printerr"], # mcheck_print
             ["abort", "malloc_printerr"], # mcheck_noprint
             ["free"],
             ["malloc"],
             ["__malloc_assert"] ]

        for seq in libc_bts:
            if self.isInBacktrace(seq, "/libc"):
                return True
        return False

    # PROBABLY_EXPLOITABLE matching methods follow

    @memoized
    def isStackOverflow(self):
        if not self.isAccessViolationSignal() or not self.target.current_instruction():
            return False
    
        # verify this is a push* instruction or
        # a call instruction where the AV is due to the "push"
        if not self.target.current_instruction().mnemonic.startswith("push") and \
           not self.target.current_instruction().mnemonic.startswith("stm"):
               return False
    
        # verify the stack pointer is outside the default stack region
        pm = self.target.procmaps().findByAddr(self.target.stack_pointer())
        if pm and pm.name == "[stack]":
            return False
    
        return True
    
    @memoized
    def isBlockMove(self):
        return False

    '''
    Helpers
    The following functions are called by multiple analyzers and are not 
    directly associated with a rule.
    '''

    @memoized
    def isJumpInstruction(self):
        ins = self.target.current_instruction()
        return ins and "pc" in ins.dest.regs and not ins.mnemonic.startswith("bl")

    @memoized
    def isBranchAv(self):
        if not self.isAccessViolationSignal():
            return False
        ins = self.target.current_instruction()
        return self.isJumpInstruction() or (ins and ins.mnemonic.startswith("bl"))

    @memoized
    def faultingAddress(self):
        if self.isJumpInstruction():
            # si_addr does not always contain a valid faulting address, but
            # jump instructions always access the dest op and GDB always displays
            # the absolute addr, so we can use the dest op instead of si_addr here.
            try:
                return self.target.current_instruction().source.eval()
            except gdb.error:
                pass # eval() will fail if we're not running (ie. --asan)
        return self.target.si_addr()

    @memoized
    def isDestAv(self):
        try:
            return getattr(self.target, "asan_operation") == "WRITE"
        except AttributeError:
            pass # not --asan
        if not self.isAccessViolationSignal():
            return False
        dest_op = getattr(self.target.current_instruction(), "dest", False)
        try:
            return dest_op and dest_op.is_pointer and \
            dest_op.eval() == self.faultingAddress()
        except gdb.error:
            return None # allow to fail for --asan

    @memoized
    def isSourceAv(self):
        try:
            return getattr(self.target, "asan_operation") == "READ"
        except AttributeError:
            pass # not --asan
        if not self.isAccessViolationSignal():
            return False
        source_op = getattr(self.target.current_instruction(), "source", False)
        try:
            return source_op and source_op.is_pointer and \
            source_op.eval() == self.faultingAddress()
        except gdb.error:
            return None # allow to fail for --asan

    @memoized
    def isUseAfterFree(self):
        return False

