Commit message: None
CVE: CVE-2016-0839
CWE: CWE-119
Vulnerability description: post_proc/volume_listener.c in mediaserver in Android 6.x before 2016-04-01 mishandles deleted effect context, which allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted media file, aka internal bug 25753245.

Project: Android

static void check_and_set_gain_dep_cal()
{

 struct listnode *node = NULL;
 float new_vol = 0.0;
 int max_level = 0;
 vol_listener_context_t *context = NULL;
 if (dumping_enabled) {
        dump_list_l();
 }

    ALOGV("%s ==> Start ...", __func__);

    list_for_each(node, &vol_effect_list) {
        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
 if ((context->state == VOL_LISTENER_STATE_ACTIVE) &&
 (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) &&
 (new_vol < (context->left_vol + context->right_vol) / 2)) {
            new_vol = (context->left_vol + context->right_vol) / 2;
 }
 }

 if (new_vol != current_vol) {
        ALOGV("%s:: Change in decision :: current volume is %f new volume is %f",
              __func__, current_vol, new_vol);

 if (send_gain_dep_cal != NULL) {
 int gain_dep_cal_level = -1;

 if (new_vol >= 1) { // max amplitude, use highest DRC level
                gain_dep_cal_level = volume_curve_gain_mapping_table[MAX_GAIN_LEVELS - 1].level;
 } else if (new_vol <= 0) {
                gain_dep_cal_level = volume_curve_gain_mapping_table[0].level;
 } else {
 for (max_level = 0; max_level + 1 < MAX_GAIN_LEVELS; max_level++) {
 if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp &&
                        new_vol >= volume_curve_gain_mapping_table[max_level].amp) {
                        gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level;
                        ALOGV("%s: volume(%f), gain dep cal selcetd %d ",
                              __func__, current_vol, gain_dep_cal_level);
 break;
 }
 }
 }

 if (gain_dep_cal_level != -1) {
 if (gain_dep_cal_level != current_gain_dep_cal_level) {
 if (!send_gain_dep_cal(gain_dep_cal_level)) {
                        ALOGE("%s: Failed to set gain dep cal level", __func__);
 } else {
 if (dumping_enabled) {
                            ALOGW("%s: (old/new) Volume (%f/%f) (old/new) level (%d/%d)",
                                  __func__, current_vol, new_vol, current_gain_dep_cal_level,
                                  gain_dep_cal_level);
 } else {
                            ALOGV("%s: Change in Cal::(old/new) Volume (%f/%f) (old/new) level (%d/%d)",
                                  __func__, current_vol, new_vol, current_gain_dep_cal_level,
                                  gain_dep_cal_level);
 }
                        current_gain_dep_cal_level = gain_dep_cal_level;
                        current_vol = new_vol;
 }
 } else {
 if (dumping_enabled) {
                        ALOGW("%s: volume changed but gain dep cal level is still the same",
                              __func__);
 } else {
                        ALOGV("%s: volume changed but gain dep cal level is still the same",
                              __func__);
 }
 }
 } else {
                ALOGW("%s: Failed to find gain dep cal level for volume %f", __func__, new_vol);
 }
 } else {
            ALOGE("%s: not able to send calibration, NULL function pointer",
                  __func__);
 }
 } else {
        ALOGV("%s:: volume not changed, stick to same config ..... ", __func__);
 }

    ALOGV("check_and_set_gain_dep_cal ==> End ");
}

Project: Android

static void dump_list_l()
{
 struct listnode *node;
 vol_listener_context_t *context;

    ALOGW("DUMP_START :: ===========");

    list_for_each(node, &vol_effect_list) {
        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
        ALOGW("%s: streamType [%s] Device [%d] state [%d] sessionID [%d] volume (L/R) [%f / %f] ",
                __func__,
                context->stream_type == MUSIC ? "MUSIC" :
                context->stream_type == RING ? "RING" :
                context->stream_type == ALARM ? "ALARM" :
                context->stream_type == VOICE_CALL ? "VOICE_CALL" :
                context->stream_type == NOTIFICATION ? "NOTIFICATION" : "--INVALID--",
                context->dev_id, context->state, context->session_id, context->left_vol,context->right_vol);
 }

    ALOGW("DUMP_END :: ===========");
}

Project: Android

static void init_once()
{
 int i = 0;
 if (initialized) {
        ALOGV("%s : already init .. do nothing", __func__);
 return;
 }

    ALOGD("%s Called ", __func__);
    pthread_mutex_init(&vol_listner_init_lock, NULL);

 if (access(PRIMARY_HAL_PATH, R_OK) == 0) {
 void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
 if (hal_lib_pointer == NULL) {
            ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
            send_gain_dep_cal = NULL;
 } else {
            ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
            send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
 if (send_gain_dep_cal == NULL) {
                ALOGE("Couldnt able to get the function symbol");
 }
 }
 } else {
        ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
        send_gain_dep_cal = NULL;
 }

 char check_dump_val[PROPERTY_VALUE_MAX];
    property_get("audio.volume.listener.dump", check_dump_val, "0");
 if (atoi(check_dump_val)) {
        dumping_enabled = true;
 }

    init_status = 0;
    list_init(&vol_effect_list);
    initialized = true;
}

Project: Android

static int lib_init()
{
    pthread_once(&once, init_once);
    ALOGV("%s Called ", __func__);
 return init_status;
}

Project: Android

static int vol_effect_command(effect_handle_t self,
 uint32_t cmd_code, uint32_t cmd_size,
 void *p_cmd_data, uint32_t *reply_size,
 void *p_reply_data)
{
 vol_listener_context_t *context = (vol_listener_context_t *)self;
 int status = 0;

    ALOGV("%s Called ", __func__);
    pthread_mutex_lock(&vol_listner_init_lock);

 if (context == NULL || context->state == VOL_LISTENER_STATE_UNINITIALIZED) {
        ALOGE("%s: %s is NULL", __func__, (context == NULL) ?
 "context" : "context->state");
        status = -EINVAL;
 goto exit;
 }

 switch (cmd_code) {
 case EFFECT_CMD_INIT:
        ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__);
 if (p_reply_data == NULL || *reply_size != sizeof(int)) {
            ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
 (p_reply_data == NULL) ? "p_reply_data is NULL" :
 "*reply_size != sizeof(int)");
 return -EINVAL;
 }
 *(int *)p_reply_data = 0;
 break;

 case EFFECT_CMD_SET_CONFIG:
        ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
 break;

 case EFFECT_CMD_GET_CONFIG:
        ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__);
 break;

 case EFFECT_CMD_RESET:
        ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__);
 break;

 case EFFECT_CMD_SET_AUDIO_MODE:
        ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__);
 break;

 case EFFECT_CMD_OFFLOAD:
        ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__);
 if (p_reply_data == NULL || *reply_size != sizeof(int)) {
            ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
 (p_reply_data == NULL) ? "p_reply_data is NULL" :
 "*reply_size != sizeof(int)");
 return -EINVAL;
 }
 *(int *)p_reply_data = 0;
 break;

 case EFFECT_CMD_ENABLE:
        ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__);
 if (p_reply_data == NULL || *reply_size != sizeof(int)) {
            ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__,
 (p_reply_data == NULL) ? "p_reply_data is NULL" :
 "*reply_size != sizeof(int)");
            status = -EINVAL;
 goto exit;
 }

 if (context->state != VOL_LISTENER_STATE_INITIALIZED) {
            ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__);
            status = -ENOSYS;
 goto exit;
 }

        context->state = VOL_LISTENER_STATE_ACTIVE;
 *(int *)p_reply_data = 0;

 if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
                check_and_set_gain_dep_cal();
 }

 break;

 case EFFECT_CMD_DISABLE:
        ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__);
 if (p_reply_data == NULL || *reply_size != sizeof(int)) {
            ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__,
 (p_reply_data == NULL) ? "p_reply_data is NULL" :
 "*reply_size != sizeof(int)");
            status = -EINVAL;
 goto exit;
 }

 if (context->state != VOL_LISTENER_STATE_ACTIVE) {
            ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__);
            status = -ENOSYS;
 goto exit;
 }

        context->state = VOL_LISTENER_STATE_INITIALIZED;
 *(int *)p_reply_data = 0;

 if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
            check_and_set_gain_dep_cal();
 }

 break;

 case EFFECT_CMD_GET_PARAM:
        ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__);
 break;

 case EFFECT_CMD_SET_PARAM:
        ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__);
 break;

 case EFFECT_CMD_SET_DEVICE:
 {
 uint32_t new_device;
 bool recompute_gain_dep_cal_Level = false;
        ALOGV("cmd called EFFECT_CMD_SET_DEVICE ");

 if (p_cmd_data == NULL) {
            ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__);
            status = -EINVAL;
 goto exit;
 }

        new_device = *(uint32_t *)p_cmd_data;
        ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)",
               __func__, context->dev_id, new_device);

 if ((context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) ||
 (new_device & AUDIO_DEVICE_OUT_SPEAKER)) {
            recompute_gain_dep_cal_Level = true;
 }

        context->dev_id = new_device;

 if (recompute_gain_dep_cal_Level) {
            check_and_set_gain_dep_cal();
 }
 }
 break;

 case EFFECT_CMD_SET_VOLUME:
 {
 float left_vol = 0, right_vol = 0;
 bool recompute_gain_dep_cal_Level = false;

        ALOGV("cmd called EFFECT_CMD_SET_VOLUME");
 if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) {
            ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ?
 "p_cmd_data is NULL" : "cmd_size issue");
            status = -EINVAL;
 goto exit;
 }

 if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
            recompute_gain_dep_cal_Level = true;
 }

        left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24);
        right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24);
        ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol,
              context->right_vol, left_vol, right_vol);

        context->left_vol = left_vol;
        context->right_vol = right_vol;

 if (recompute_gain_dep_cal_Level) {
            check_and_set_gain_dep_cal();
 }
 }
 break;

 default:
        ALOGW("volume_listener_command invalid command %d", cmd_code);
        status = -ENOSYS;
 break;
 }

exit:
    pthread_mutex_unlock(&vol_listner_init_lock);
 return status;
}

Project: Android

static int vol_effect_get_descriptor(effect_handle_t   self,
 effect_descriptor_t *descriptor)
{
 vol_listener_context_t *context = (vol_listener_context_t *)self;
    ALOGV("%s Called ", __func__);

 if (descriptor == NULL) {
        ALOGE("%s: descriptor is NULL", __func__);
 return -EINVAL;
 }

 *descriptor = *context->desc;
 return 0;
}

Project: Android

static int vol_effect_process(effect_handle_t self,
 audio_buffer_t *in_buffer,
 audio_buffer_t *out_buffer)
{
 int status = 0;
    ALOGV("%s Called ", __func__);

 vol_listener_context_t *context = (vol_listener_context_t *)self;
    pthread_mutex_lock(&vol_listner_init_lock);

 if (context->state != VOL_LISTENER_STATE_ACTIVE) {
        ALOGE("%s: state is not active .. return error", __func__);
        status = -EINVAL;
 goto exit;
 }

 if (in_buffer->raw != out_buffer->raw) {
        memcpy(out_buffer->raw, in_buffer->raw, out_buffer->frameCount * 2 * sizeof(int16_t));
 } else {
        ALOGW("%s: something wrong, didn't handle in_buffer and out_buffer same address case",
              __func__);
 }

exit:
    pthread_mutex_unlock(&vol_listner_init_lock);
 return status;
}

Project: Android

static int vol_prc_lib_create(const effect_uuid_t *uuid,
 int32_t session_id,
 int32_t io_id,
 effect_handle_t *p_handle)
{
 int itt = 0;
 vol_listener_context_t *context = NULL;

    ALOGV("volume_prc_lib_create .. called ..");

 if (lib_init() != 0) {
 return init_status;
 }

 if (p_handle == NULL || uuid == NULL) {
        ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
 return -EINVAL;
 }

    context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t));

 if (context == NULL) {
        ALOGE("%s: failed to allocate for context .. oops !!", __func__);
 return -EINVAL;
 }

 for (itt = 0; descriptors[itt] != NULL; itt++) {
 if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
            context->desc = descriptors[itt];
            context->stream_type = itt;
            PRINT_STREAM_TYPE(itt)
 break;
 }
 }

 if (descriptors[itt] == NULL) {
        ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
        free(context);
 return -EINVAL;
 }

    ALOGV("%s CREATED_CONTEXT %p", __func__, context);

    context->itfe = &effect_interface;
    context->state = VOL_LISTENER_STATE_INITIALIZED;
    context->dev_id = AUDIO_DEVICE_NONE;
    context->session_id = session_id;

    pthread_mutex_lock(&vol_listner_init_lock);
    list_add_tail(&vol_effect_list, &context->effect_list_node);

 if (dumping_enabled) {
        dump_list_l();
 }

    pthread_mutex_unlock(&vol_listner_init_lock);

 *p_handle = (effect_handle_t)context;
 return 0;
}