/* **********************************************************
 * Copyright (c) 2013-2021 Google, Inc.  All rights reserved.
 * Copyright (c) 2010 VMware, Inc.  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.
 *
 * * Neither the name of VMware, 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 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 VMWARE, INC. 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.
 */

#ifndef _DR_PROBE_H_
#define _DR_PROBE_H_ 1

/****************************************************************************
 * Probe API
 */
/**
 * @file dr_probe.h
 * @brief Support for the Probe API.
 *
 *  Describes all the data types and functions associated with the \ref
 * API_probe.
 */

#ifdef __cplusplus
extern "C" {
#endif

#ifndef DR_API
#    define DR_API
#endif

/**
 * Describes the status of a probe at any given point.  The status is returned
 * by dr_register_probes() in the dr_probe_desc_t structure for each probe
 * specified.  It can be obtained for individual probes by calling
 * dr_get_probe_status().
 */
typedef enum {
    /* Error codes. */

    /** Exceptions during callback execution and other unknown errors. */
    DR_PROBE_STATUS_ERROR = 1,

    /** An invalid or unknown probe ID was specified with dr_get_probe_status(). */
    DR_PROBE_STATUS_INVALID_ID = 2,

    /* All the invalid states listed below may arise statically (at the
     * time of parsing the probes, i.e., inside dr_register_probes() or
     * dynamically (i.e., when modules are loaded or unloaded)).
     */

    /** The numeric virtual address specified for the probe insertion location
     * or the callback function is invalid.
     */
    DR_PROBE_STATUS_INVALID_VADDR = 3,

    /** The pointer to the name of the library containing the probe insertion
     * location or the callback function is invalid or the library containing
     * the callback function can't be loaded.
     */
    DR_PROBE_STATUS_INVALID_LIB = 4,

    /** The library offset for either the probe insertion location or the
     * callback function is invalid; for ex., if the offset is out of bounds.
     */
    DR_PROBE_STATUS_INVALID_LIB_OFFS = 5,

    /** The pointer to the name of the exported function, where the probe is to
     * be inserted or which is the callback function, is invalid or the exported
     * function doesn't exist.
     */
    DR_PROBE_STATUS_INVALID_FUNC = 6,

    /* Codes for pending cases, i.e., valid probes haven't been inserted
     * because certain events haven't transpired.
     */

    /** The numeric virtual address specified for the probe insertion location
     * or the callback function isn't executable.  This may be so at the time
     * of probe registration or latter if the memory protections are changed.
     * An inserted probe might reach this state if the probe insertion point or
     * the callback function is made non-executable after being executable.
     */
    DR_PROBE_STATUS_VADDR_NOT_EXEC = 7,

    /** The library where the probe is to be inserted isn't in the process. */
    DR_PROBE_STATUS_LIB_NOT_SEEN = 8,

    /** Execution hasn't reached the probe insertion point yet.  This is valid
     * for Code Manipulation mode only because in that mode probes are inserted
     * only in the dynamic instruction stream.
     */
    DR_PROBE_STATUS_WAITING_FOR_EXEC = 9,

    /** Either the library where the probe is to be inserted has been unloaded
     * or the library containing the callback function has been unloaded.
     */
    DR_PROBE_STATUS_LIB_UNLOADED = 10,

    /* Miscellaneous status codes. */

    /** Probe was successfully inserted. */
    DR_PROBE_STATUS_INJECTED = 11,

    /** One or more aspects of the probe aren't supported as of now. */
    DR_PROBE_STATUS_UNSUPPORTED = 12,

#ifdef DYNAMORIO_INTERNAL
    /* DON'T CHANGE THE VALUES OF THE DR_* CONSTANTS DEFINED ABOVE.  They are
     * exported to clients, whereas constants in this ifdef aren't.  Any change
     * to those values will likely break old clients with newer versions of DR
     * (backward compatibility).  New status codes should go after
     * DR_PROBE_STATUS_UNSUPPORTED.
     */
    /* Note: constants are numbered to prevent the compiler from resetting the
     * sequence based on the symbolic assignments below.  HOTP_INJECT_DETECT
     * ended up getting the same number as one the values above!  Ditto with
     * HOTP_INJECT_OFF.  Though these duplications only broke the tools build
     * they can cause subtle runtime errors, so forcing numbers.
     */

    /* The constants below are used only for LiveShields.  */

    /* Defines the current injection status of a policy, i.e., was it injected
     * or not, why and why not?  Today we don't distinguish the reasons for
     * error cases, i.e., all of them are rolled into one.
     *
     * Constants list from most important status to least, from a reporting
     * point of view; don't change this arbitrarily.
     *
     * CAUTION: Any changes to this will break drview, so they must be kept in
     * sync.
     */

    /* Deactivation, exception, error, etc. */
    HOTP_INJECT_ERROR = DR_PROBE_STATUS_ERROR,

    /* Completely injected in protect mode. */
    HOTP_INJECT_PROTECT = DR_PROBE_STATUS_INJECTED,

    /* Completely injected in detect mode.  Not applicable to probes as they
     * don't have detectors.  Restart numbering at 100 to give enough room for
     * future probe status constants.
     */
    HOTP_INJECT_DETECT = 100,

    /* One or more patch points in a vulnerability have been patched, but not
     * all, yet.  N/A to probes as they can't group multiple patch points.
     */
    HOTP_INJECT_IN_PROGRESS = 101,

    /* Vulnerability was matched and is ready for injection, but no patch point
     * has been seen yet.
     */
    HOTP_INJECT_PENDING = DR_PROBE_STATUS_WAITING_FOR_EXEC,

    /* Vulnerability hasn't been matched yet.  May mean that it is not yet
     * vulnerable (because all dlls haven't been loaded) or just not vulnerable
     * at all;  there is no way to distinguish between the two.
     */
    HOTP_INJECT_NO_MATCH = DR_PROBE_STATUS_LIB_NOT_SEEN,

    /*
    TODO: must distinguish between no match & vulnerable vs. no match & not
          vulnerable; future work if needed.
    HOTP_INJECT_NO_MATCH_VULNERABLE,
    HOTP_INJECT_NO_MATCH_NOT_VULNERABLE,
    */

    HOTP_INJECT_OFF = 102 /* Policy has been turned off, so no injection. */
#endif                    /* DYNAMORIO_INTERNAL */
} dr_probe_status_t;

/** Specifies what type of computation to use when computing the address of a
 * probe insertion point or callback function.
 */
typedef enum {
    /** Use the raw virtual address specified. */
    DR_PROBE_ADDR_VIRTUAL,

    /** Compute address by adding the offset specified to the base of the
     * library specified.
     *
     * For probe insertion, if library isn't loaded, the insertion will be
     * aborted.  For computing callback function address if the library isn't
     * loaded, it will be loaded and then the address computation will be done;
     * if it can't be loaded, probe request is aborted.
     */
    DR_PROBE_ADDR_LIB_OFFS,

    /** Compute address by getting the address of the specified exported
     * function from the specified library.
     *
     * If the exported function specified isn't available either for the probe
     * insertion point or for the callback function, the probe insertion is
     * aborted.  For computing callback function address if the library isn't
     * loaded, it will be loaded and then the address computation will be done;
     * if it can't be loaded, probe request is aborted. */
    DR_PROBE_ADDR_EXP_FUNC,
} dr_probe_addr_t;

/** Defines the location where a probe is to be inserted or callback function
 * defined as an offset within a library. */
typedef struct {
    /** IN - Full name of the library. */
    /* FIXME PR 533522: explicitly specify what type of name should be used
     * here: full path, dr_module_preferred_name(), pe (exports) name, what?
     * seems broken since need full path to load a lib but that won't match?
     */
    char *library;

    /** IN - Offset to use inside library.  If out of bounds of library, probe
     * request is aborted.  Offset can point to non text location as long as it
     * is marked executable (i.e., ..x). */
    app_rva_t offset;
} dr_probe_lib_offs_t;

/** Defines the location where a probe is to be inserted or callback function
 * defined as an exported function within a library. */
typedef struct {
    /** IN - Full name of the library. */
    /* FIXME PR 533522: explicitly specify what type of name should be used
     * here: full path, dr_module_preferred_name(), pe (exports) name, what?
     * seems broken since need full path to load a lib but that won't match?
     */
    char *library;

    /** IN - Name of exported function inside library.  If the function can't
     * be found in the library, then this probe request is aborted. */
    char *func;
} dr_probe_exp_func_t;

/** Defines a memory location where a probe is to be inserted or where a
 * callback function exists.  One of three types of address computation as
 * described by dr_probe_addr_t is used to identify the location.
 */
typedef struct {
    /** IN - Specifies the type of address computation to use. */
    dr_probe_addr_t type;

    union {
        /** IN - Raw virtual address in the process space. */
        app_pc vaddr;

        /** IN - Library offset in the process space. */
        dr_probe_lib_offs_t lib_offs;

        /** IN - Exported function in the process space. */
        dr_probe_exp_func_t exp_func;
    };
} dr_probe_location_t;

/* TODO: hotp_exec_status_t in hotpatch_interface.h is what's really used
 * in the code so once we start adding actual values here we should merge
 * the two.
 */
/**
 * Specifies what action to take upon return of a probe callback function.
 * Additional options will be added in future releases.
 */
typedef enum {
    /** Continue execution normally after the probe. */
    DR_PROBE_RETURN_NORMAL = 0,
} dr_probe_return_t;

/**
 * This structure describes the characteristics of a probe.  It is used to add,
 * update, and remove probes using dr_register_probes().
 */
typedef struct {
    /**
     * IN - Pointer to the name of the probe.  This string does not need
     * to be persistent beyond the call to dr_register_probes(): a copy will
     * be made.
     */
    char *name;

    /**
     * IN - Location where the probe is to be inserted.  If inserting inside a
     * library, the insertion will be done only if the library is loaded, the
     * location falls within its bounds and the location is marked executable.
     * If inserting outside a library the memory location should be allocated
     * and marked executable.  If neither, the probe will be put in a pending
     * state where its id will be NULL and its status reflecting the state.
     */
    dr_probe_location_t insert_loc;

    /**
     * IN - Location of the callback function.  If callback is inside a
     * library, the library location will be used if it is within its bounds
     * and is marked executable; if library isn't loaded, an attempt will be
     * made to load it.  If using a raw virtual address, then that location
     * should be mapped and marked executable.  If neither is true, the probe
     * insertion or update will be aborted and status updated accordingly.
     *
     * The callback function itself should have this signature:
     *
     *   dr_probe_return_t probe_callback(priv_mcontext_t *mc);
     *
     * Note that the \p xip field of the \p priv_mcontext_t passed in will
     * NOT be set.
     */
    dr_probe_location_t callback_func;

    /** OUT - Upon successful probe insertion a unique id will be created. */
    unsigned int id;

    /** OUT - Specifies the current status of the probe. */
    dr_probe_status_t status;
} dr_probe_desc_t;

/******************************************************************************/
DR_API
/**
 * Allows the caller to insert probes into specified executable memory
 * locations in the process address space.  Upon subsequent execution of
 * instructions at these memory locations the appropriate probes will be
 * triggered and the corresponding callback functions will be invoked.
 * Subsequent calls to dr_register_probes() will allow the caller to remove,
 * update and add more probes.
 *
 * @param       probes  Pointer to an array of probe descriptors of type
 *                      dr_probe_desc_t.  Each descriptor describes the
 *                      location of the probe, the callback function and other
 *                      data.  This is an in/out parameter, see dr_probe_desc_t
 *                      for details.  If probes isn't NULL, points to valid
 *                      memory and num_probes is greater than 0, id and status
 *                      for each probe are set in the corresponding
 *                      dr_probe_desc_t.  If probes is NULL or num_probes is 0,
 *                      nothing is set in probes.  Invalid memory may trigger
 *                      an exception.
 *
 * @param [in]  num_probes  Specifies the number of probe descriptors in
 *                          the array pointed to by probes.
 *
 * \remarks
 * If a probe definition is invalid, it will not be registered, i.e. DynamoRIO
 *  will not retain its definition.  The error code will be returned in the
 *  status field in of that probe's dr_probe_desc_t element and the
 *  corresponding id field in dr_probe_desc_t is set to NULL.
 *
 * \remarks
 * When a client calls dr_register_probes() from dr_client_main() (which is the
 *  earliest dr_register_probes() can be called), not all valid probes are
 *  guaranteed to be inserted upon retrun from dr_register_probes().  Some
 *  valid probes may not be inserted if the target module has not been loaded,
 *  the insertion point is not executable, or the memory is otherwise
 *  inaccessible.  In such cases, DynamoRIO retains all valid probe
 *  information and inserts these probes when the memory locations become
 *  available.
 *
 * \remarks
 * When dr_register_probes() is called after dr_client_main(), the behavior is
 *  identical to being called from dr_client_main() with one caveat: even valid probes
 *  aren't guaranteed to be registered when dr_register_probes() returns.
 *  However, valid probes will be registered within a few milliseconds usually.
 *  To prevent performance and potential deadlock issues, it is recommended
 *  that a client shouldn't wait in a loop till the probe status changes.
 *  Instead, clients should check probe status at a later callback.
 *
 * \remarks
 * A client can determine the status of a registered probe in one of two ways.
 *  1) Read it from the status field in the associated dr_probe_desc_t element
 *  when dr_register_probes() returns, or 2) call dr_get_probe_status() with
 *  the id of the probe.
 *
 * \remarks
 * To add, remove or update currently registered probes dr_register_probes()
 *  should be called again with a new set of probe definitions.  The new list
 *  of probes completely replaces the existing probes.  That is, existing
 *  probes not specified in the new list are removed from the process.
 *
 * \remarks
 * The probe insertion address has limitations.  5 bytes beginning at the start
 *  of a probe insertion address should have specific characteristics.  No
 *  instruction should straddle this start of this region, i.e., the probe
 *  insertion address should be the beginning of an instruction.  Also, no flow
 *  of control should jump into the middle of this 5 byte region beginning at
 *  the probe insertion address.  Further, there should be no int instructions
 *  in this region.  call and jump instructions are allowed in this region as
 *  long as they don't terminate before the end of the region, i.e., no other
 *  instruction should start after them in this region (as it will allow
 *  control flow to return to the middle of this region).  If these rules are
 *  not adhered to the results are be unspecified; may result in process crash.
 *  The above mentioned restrictions hold only when using \ref API_probe not
 *  when using API_BT or both simultaneously.
 *
 * \remarks
 * If only the \ref API_probe is used 5 bytes starting at the probe insertion
 *  address will be modified.  If the process will read this memory then the
 *  probe should be moved to another location or removed to avoid unknown
 *  changes in process behavior.  If the client will read this memory, then it
 *  has to do so before requesting probe insertion.
 *
 * \see dr_get_probe_status().
 */
void
dr_register_probes(dr_probe_desc_t *probes, unsigned int num_probes);

DR_API
/** Used to get the current status of a probe.
 *
 * \param[in]   id  Unique identifier of the probe for which status is desired.
 *
 * \param[out]  status  Pointer to user allocated data of type dr_probe_status_t
 *                      in which the status of the probe specified by id is
 *                      returned.  If id matches and status isn't NULL, the
 *                      status for the matching probe is returned.  If id
 *                      doesn't match or if status is NULL, nothing is
 *                      returned in status.
 *
 * \return      If id matches and status is copied successfully, 1 is returned,
 *              else 0 is returned.
 */
int
dr_get_probe_status(unsigned int id, dr_probe_status_t *status);

#ifdef __cplusplus
}
#endif

#endif /* _DR_PROBE_H_ */