bpf: Fix ringbuf helper function compatibility

Commit 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support
for it") extended check_map_func_compatibility() by enforcing map -> helper
function match, but not helper -> map type match.

Due to this all of the bpf_ringbuf_*() helper functions could be used with
a wrong map type such as array or hash map, leading to invalid access due
to type confusion.

Also, both BPF_FUNC_ringbuf_{submit,discard} have ARG_PTR_TO_ALLOC_MEM as
argument and not a BPF map. Therefore, their check_map_func_compatibility()
presence is incorrect since it's only for map type checking.

Fixes: 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support for it")
Reported-by: Ryota Shiga (Flatt Security)
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>

static int check_map_func_compatibility(struct bpf_verifier_env *env,
					struct bpf_map *map, int func_id)
{
	if (!map)
		return 0;

	/* We need a two way check, first is from map perspective ... */
	switch (map->map_type) {
	case BPF_MAP_TYPE_PROG_ARRAY:
		if (func_id != BPF_FUNC_tail_call)
			goto error;
		break;
	case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
		if (func_id != BPF_FUNC_perf_event_read &&
		    func_id != BPF_FUNC_perf_event_output &&
		    func_id != BPF_FUNC_skb_output &&
		    func_id != BPF_FUNC_perf_event_read_value &&
		    func_id != BPF_FUNC_xdp_output)
			goto error;
		break;
	case BPF_MAP_TYPE_RINGBUF:
		if (func_id != BPF_FUNC_ringbuf_output &&
		    func_id != BPF_FUNC_ringbuf_reserve &&
		    func_id != BPF_FUNC_ringbuf_submit &&
		    func_id != BPF_FUNC_ringbuf_discard &&
		    func_id != BPF_FUNC_ringbuf_query)
			goto error;
		break;
	case BPF_MAP_TYPE_STACK_TRACE:
		if (func_id != BPF_FUNC_get_stackid)
			goto error;
		break;
	case BPF_MAP_TYPE_CGROUP_ARRAY:
		if (func_id != BPF_FUNC_skb_under_cgroup &&
		    func_id != BPF_FUNC_current_task_under_cgroup)
			goto error;
		break;
	case BPF_MAP_TYPE_CGROUP_STORAGE:
	case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
		if (func_id != BPF_FUNC_get_local_storage)
			goto error;
		break;
	case BPF_MAP_TYPE_DEVMAP:
	case BPF_MAP_TYPE_DEVMAP_HASH:
		if (func_id != BPF_FUNC_redirect_map &&
		    func_id != BPF_FUNC_map_lookup_elem)
			goto error;
		break;
	/* Restrict bpf side of cpumap and xskmap, open when use-cases
	 * appear.
	 */
	case BPF_MAP_TYPE_CPUMAP:
		if (func_id != BPF_FUNC_redirect_map)
			goto error;
		break;
	case BPF_MAP_TYPE_XSKMAP:
		if (func_id != BPF_FUNC_redirect_map &&
		    func_id != BPF_FUNC_map_lookup_elem)
			goto error;
		break;
	case BPF_MAP_TYPE_ARRAY_OF_MAPS:
	case BPF_MAP_TYPE_HASH_OF_MAPS:
		if (func_id != BPF_FUNC_map_lookup_elem)
			goto error;
		break;
	case BPF_MAP_TYPE_SOCKMAP:
		if (func_id != BPF_FUNC_sk_redirect_map &&
		    func_id != BPF_FUNC_sock_map_update &&
		    func_id != BPF_FUNC_map_delete_elem &&
		    func_id != BPF_FUNC_msg_redirect_map &&
		    func_id != BPF_FUNC_sk_select_reuseport &&
		    func_id != BPF_FUNC_map_lookup_elem &&
		    !may_update_sockmap(env, func_id))
			goto error;
		break;
	case BPF_MAP_TYPE_SOCKHASH:
		if (func_id != BPF_FUNC_sk_redirect_hash &&
		    func_id != BPF_FUNC_sock_hash_update &&
		    func_id != BPF_FUNC_map_delete_elem &&
		    func_id != BPF_FUNC_msg_redirect_hash &&
		    func_id != BPF_FUNC_sk_select_reuseport &&
		    func_id != BPF_FUNC_map_lookup_elem &&
		    !may_update_sockmap(env, func_id))
			goto error;
		break;
	case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
		if (func_id != BPF_FUNC_sk_select_reuseport)
			goto error;
		break;
	case BPF_MAP_TYPE_QUEUE:
	case BPF_MAP_TYPE_STACK:
		if (func_id != BPF_FUNC_map_peek_elem &&
		    func_id != BPF_FUNC_map_pop_elem &&
		    func_id != BPF_FUNC_map_push_elem)
			goto error;
		break;
	case BPF_MAP_TYPE_SK_STORAGE:
		if (func_id != BPF_FUNC_sk_storage_get &&
		    func_id != BPF_FUNC_sk_storage_delete)
			goto error;
		break;
	case BPF_MAP_TYPE_INODE_STORAGE:
		if (func_id != BPF_FUNC_inode_storage_get &&
		    func_id != BPF_FUNC_inode_storage_delete)
			goto error;
		break;
	case BPF_MAP_TYPE_TASK_STORAGE:
		if (func_id != BPF_FUNC_task_storage_get &&
		    func_id != BPF_FUNC_task_storage_delete)
			goto error;
		break;
	default:
		break;
	}

	/* ... and second from the function itself. */
	switch (func_id) {
	case BPF_FUNC_tail_call:
		if (map->map_type != BPF_MAP_TYPE_PROG_ARRAY)
			goto error;
		if (env->subprog_cnt > 1 && !allow_tail_call_in_subprogs(env)) {
			verbose(env, "tail_calls are not allowed in non-JITed programs with bpf-to-bpf calls\n");
			return -EINVAL;
		}
		break;
	case BPF_FUNC_perf_event_read:
	case BPF_FUNC_perf_event_output:
	case BPF_FUNC_perf_event_read_value:
	case BPF_FUNC_skb_output:
	case BPF_FUNC_xdp_output:
		if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
			goto error;
		break;
	case BPF_FUNC_get_stackid:
		if (map->map_type != BPF_MAP_TYPE_STACK_TRACE)
			goto error;
		break;
	case BPF_FUNC_current_task_under_cgroup:
	case BPF_FUNC_skb_under_cgroup:
		if (map->map_type != BPF_MAP_TYPE_CGROUP_ARRAY)
			goto error;
		break;
	case BPF_FUNC_redirect_map:
		if (map->map_type != BPF_MAP_TYPE_DEVMAP &&
		    map->map_type != BPF_MAP_TYPE_DEVMAP_HASH &&
		    map->map_type != BPF_MAP_TYPE_CPUMAP &&
		    map->map_type != BPF_MAP_TYPE_XSKMAP)
			goto error;
		break;
	case BPF_FUNC_sk_redirect_map:
	case BPF_FUNC_msg_redirect_map:
	case BPF_FUNC_sock_map_update:
		if (map->map_type != BPF_MAP_TYPE_SOCKMAP)
			goto error;
		break;
	case BPF_FUNC_sk_redirect_hash:
	case BPF_FUNC_msg_redirect_hash:
	case BPF_FUNC_sock_hash_update:
		if (map->map_type != BPF_MAP_TYPE_SOCKHASH)
			goto error;
		break;
	case BPF_FUNC_get_local_storage:
		if (map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE &&
		    map->map_type != BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)
			goto error;
		break;
	case BPF_FUNC_sk_select_reuseport:
		if (map->map_type != BPF_MAP_TYPE_REUSEPORT_SOCKARRAY &&
		    map->map_type != BPF_MAP_TYPE_SOCKMAP &&
		    map->map_type != BPF_MAP_TYPE_SOCKHASH)
			goto error;
		break;
	case BPF_FUNC_map_peek_elem:
	case BPF_FUNC_map_pop_elem:
	case BPF_FUNC_map_push_elem:
		if (map->map_type != BPF_MAP_TYPE_QUEUE &&
		    map->map_type != BPF_MAP_TYPE_STACK)
			goto error;
		break;
	case BPF_FUNC_sk_storage_get:
	case BPF_FUNC_sk_storage_delete:
		if (map->map_type != BPF_MAP_TYPE_SK_STORAGE)
			goto error;
		break;
	case BPF_FUNC_inode_storage_get:
	case BPF_FUNC_inode_storage_delete:
		if (map->map_type != BPF_MAP_TYPE_INODE_STORAGE)
			goto error;
		break;
	case BPF_FUNC_task_storage_get:
	case BPF_FUNC_task_storage_delete:
		if (map->map_type != BPF_MAP_TYPE_TASK_STORAGE)
			goto error;
		break;
	default:
		break;
	}

	return 0;
error:
	verbose(env, "cannot pass map_type %d into func %s#%d\n",
		map->map_type, func_id_name(func_id), func_id);
	return -EINVAL;
}

This vulnerability allows local attackers to escalate privileges on affected installations of Linux Kernel 5.14-rc3. An attacker must first obtain the ability to execute low-privileged code on the target system in order to exploit this vulnerability. The specific flaw exists within the handling of eBPF programs. The issue results from the lack of proper validation of user-supplied eBPF programs, which can result in a type confusion condition. An attacker can leverage this vulnerability to escalate privileges and execute arbitrary code in the context of the kernel. Was ZDI-CAN-14689.


static int check_map_func_compatibility(struct bpf_verifier_env *env,
					struct bpf_map *map, int func_id)
{
	if (!map)
		return 0;

	/* We need a two way check, first is from map perspective ... */
	switch (map->map_type) {
	case BPF_MAP_TYPE_PROG_ARRAY:
		if (func_id != BPF_FUNC_tail_call)
			goto error;
		break;
	case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
		if (func_id != BPF_FUNC_perf_event_read &&
		    func_id != BPF_FUNC_perf_event_output &&
		    func_id != BPF_FUNC_skb_output &&
		    func_id != BPF_FUNC_perf_event_read_value &&
		    func_id != BPF_FUNC_xdp_output)
			goto error;
		break;
	case BPF_MAP_TYPE_RINGBUF:
		if (func_id != BPF_FUNC_ringbuf_output &&
		    func_id != BPF_FUNC_ringbuf_reserve &&
		    func_id != BPF_FUNC_ringbuf_query)
			goto error;
		break;
	case BPF_MAP_TYPE_STACK_TRACE:
		if (func_id != BPF_FUNC_get_stackid)
			goto error;
		break;
	case BPF_MAP_TYPE_CGROUP_ARRAY:
		if (func_id != BPF_FUNC_skb_under_cgroup &&
		    func_id != BPF_FUNC_current_task_under_cgroup)
			goto error;
		break;
	case BPF_MAP_TYPE_CGROUP_STORAGE:
	case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
		if (func_id != BPF_FUNC_get_local_storage)
			goto error;
		break;
	case BPF_MAP_TYPE_DEVMAP:
	case BPF_MAP_TYPE_DEVMAP_HASH:
		if (func_id != BPF_FUNC_redirect_map &&
		    func_id != BPF_FUNC_map_lookup_elem)
			goto error;
		break;
	/* Restrict bpf side of cpumap and xskmap, open when use-cases
	 * appear.
	 */
	case BPF_MAP_TYPE_CPUMAP:
		if (func_id != BPF_FUNC_redirect_map)
			goto error;
		break;
	case BPF_MAP_TYPE_XSKMAP:
		if (func_id != BPF_FUNC_redirect_map &&
		    func_id != BPF_FUNC_map_lookup_elem)
			goto error;
		break;
	case BPF_MAP_TYPE_ARRAY_OF_MAPS:
	case BPF_MAP_TYPE_HASH_OF_MAPS:
		if (func_id != BPF_FUNC_map_lookup_elem)
			goto error;
		break;
	case BPF_MAP_TYPE_SOCKMAP:
		if (func_id != BPF_FUNC_sk_redirect_map &&
		    func_id != BPF_FUNC_sock_map_update &&
		    func_id != BPF_FUNC_map_delete_elem &&
		    func_id != BPF_FUNC_msg_redirect_map &&
		    func_id != BPF_FUNC_sk_select_reuseport &&
		    func_id != BPF_FUNC_map_lookup_elem &&
		    !may_update_sockmap(env, func_id))
			goto error;
		break;
	case BPF_MAP_TYPE_SOCKHASH:
		if (func_id != BPF_FUNC_sk_redirect_hash &&
		    func_id != BPF_FUNC_sock_hash_update &&
		    func_id != BPF_FUNC_map_delete_elem &&
		    func_id != BPF_FUNC_msg_redirect_hash &&
		    func_id != BPF_FUNC_sk_select_reuseport &&
		    func_id != BPF_FUNC_map_lookup_elem &&
		    !may_update_sockmap(env, func_id))
			goto error;
		break;
	case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
		if (func_id != BPF_FUNC_sk_select_reuseport)
			goto error;
		break;
	case BPF_MAP_TYPE_QUEUE:
	case BPF_MAP_TYPE_STACK:
		if (func_id != BPF_FUNC_map_peek_elem &&
		    func_id != BPF_FUNC_map_pop_elem &&
		    func_id != BPF_FUNC_map_push_elem)
			goto error;
		break;
	case BPF_MAP_TYPE_SK_STORAGE:
		if (func_id != BPF_FUNC_sk_storage_get &&
		    func_id != BPF_FUNC_sk_storage_delete)
			goto error;
		break;
	case BPF_MAP_TYPE_INODE_STORAGE:
		if (func_id != BPF_FUNC_inode_storage_get &&
		    func_id != BPF_FUNC_inode_storage_delete)
			goto error;
		break;
	case BPF_MAP_TYPE_TASK_STORAGE:
		if (func_id != BPF_FUNC_task_storage_get &&
		    func_id != BPF_FUNC_task_storage_delete)
			goto error;
		break;
	default:
		break;
	}

	/* ... and second from the function itself. */
	switch (func_id) {
	case BPF_FUNC_tail_call:
		if (map->map_type != BPF_MAP_TYPE_PROG_ARRAY)
			goto error;
		if (env->subprog_cnt > 1 && !allow_tail_call_in_subprogs(env)) {
			verbose(env, "tail_calls are not allowed in non-JITed programs with bpf-to-bpf calls\n");
			return -EINVAL;
		}
		break;
	case BPF_FUNC_perf_event_read:
	case BPF_FUNC_perf_event_output:
	case BPF_FUNC_perf_event_read_value:
	case BPF_FUNC_skb_output:
	case BPF_FUNC_xdp_output:
		if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
			goto error;
		break;
	case BPF_FUNC_ringbuf_output:
	case BPF_FUNC_ringbuf_reserve:
	case BPF_FUNC_ringbuf_query:
		if (map->map_type != BPF_MAP_TYPE_RINGBUF)
			goto error;
		break;
	case BPF_FUNC_get_stackid:
		if (map->map_type != BPF_MAP_TYPE_STACK_TRACE)
			goto error;
		break;
	case BPF_FUNC_current_task_under_cgroup:
	case BPF_FUNC_skb_under_cgroup:
		if (map->map_type != BPF_MAP_TYPE_CGROUP_ARRAY)
			goto error;
		break;
	case BPF_FUNC_redirect_map:
		if (map->map_type != BPF_MAP_TYPE_DEVMAP &&
		    map->map_type != BPF_MAP_TYPE_DEVMAP_HASH &&
		    map->map_type != BPF_MAP_TYPE_CPUMAP &&
		    map->map_type != BPF_MAP_TYPE_XSKMAP)
			goto error;
		break;
	case BPF_FUNC_sk_redirect_map:
	case BPF_FUNC_msg_redirect_map:
	case BPF_FUNC_sock_map_update:
		if (map->map_type != BPF_MAP_TYPE_SOCKMAP)
			goto error;
		break;
	case BPF_FUNC_sk_redirect_hash:
	case BPF_FUNC_msg_redirect_hash:
	case BPF_FUNC_sock_hash_update:
		if (map->map_type != BPF_MAP_TYPE_SOCKHASH)
			goto error;
		break;
	case BPF_FUNC_get_local_storage:
		if (map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE &&
		    map->map_type != BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)
			goto error;
		break;
	case BPF_FUNC_sk_select_reuseport:
		if (map->map_type != BPF_MAP_TYPE_REUSEPORT_SOCKARRAY &&
		    map->map_type != BPF_MAP_TYPE_SOCKMAP &&
		    map->map_type != BPF_MAP_TYPE_SOCKHASH)
			goto error;
		break;
	case BPF_FUNC_map_peek_elem:
	case BPF_FUNC_map_pop_elem:
	case BPF_FUNC_map_push_elem:
		if (map->map_type != BPF_MAP_TYPE_QUEUE &&
		    map->map_type != BPF_MAP_TYPE_STACK)
			goto error;
		break;
	case BPF_FUNC_sk_storage_get:
	case BPF_FUNC_sk_storage_delete:
		if (map->map_type != BPF_MAP_TYPE_SK_STORAGE)
			goto error;
		break;
	case BPF_FUNC_inode_storage_get:
	case BPF_FUNC_inode_storage_delete:
		if (map->map_type != BPF_MAP_TYPE_INODE_STORAGE)
			goto error;
		break;
	case BPF_FUNC_task_storage_get:
	case BPF_FUNC_task_storage_delete:
		if (map->map_type != BPF_MAP_TYPE_TASK_STORAGE)
			goto error;
		break;
	default:
		break;
	}

	return 0;
error:
	verbose(env, "cannot pass map_type %d into func %s#%d\n",
		map->map_type, func_id_name(func_id), func_id);
	return -EINVAL;
}


Ensure that the SrcList_item.colUsed field is set correctly (set to have a
1 for all columns of the table) when a generated column appears in the USING
clause of a join.

FossilOrigin-Name: 1923efb283e8840fa7436eb20b9d2174ef7cace1690d3b97b572a0db2048b8e3

Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
  Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
  if( p ){
    struct SrcList_item *pItem = &pSrc->a[iSrc];
    p->y.pTab = pItem->pTab;
    p->iTable = pItem->iCursor;
    if( p->y.pTab->iPKey==iCol ){
      p->iColumn = -1;
    }else{
      p->iColumn = (ynVar)iCol;
      testcase( iCol==BMS );
      testcase( iCol==BMS-1 );
      pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
    }
  }
  return p;
}

pragma.c in SQLite through 3.30.1 mishandles NOT NULL in an integrity_check PRAGMA command in certain cases of generated columns.


Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
  Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
  if( p ){
    struct SrcList_item *pItem = &pSrc->a[iSrc];
    Table *pTab = p->y.pTab = pItem->pTab;
    p->iTable = pItem->iCursor;
    if( p->y.pTab->iPKey==iCol ){
      p->iColumn = -1;
    }else{
      p->iColumn = (ynVar)iCol;
      if( pTab->tabFlags & TF_HasGenerated ){
        Column *pColumn = pTab->aCol + iCol;
        if( pColumn->colFlags & COLFLAG_GENERATED ){
          testcase( pTab->nCol==63 );
          testcase( pTab->nCol==64 );
          if( pTab->nCol>=64 ){
            pItem->colUsed = ALLBITS;
          }else{
            pItem->colUsed = MASKBIT(pTab->nCol)-1;
          }
        }
      }else{
        testcase( iCol==BMS );
        testcase( iCol==BMS-1 );
        pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
      }
    }
  }
  return p;
}


Fix bug #73065: Out-Of-Bounds Read in php_wddx_push_element of wddx.c

 */
static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
{
	st_entry ent;
	wddx_stack *stack = (wddx_stack *)user_data;

	if (!strcmp(name, EL_PACKET)) {
		int i;

		if (atts) for (i=0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_VERSION)) {
				/* nothing for now */
			}
		}
	} else if (!strcmp(name, EL_STRING)) {
		ent.type = ST_STRING;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_STRING;
		Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
		Z_STRLEN_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_BINARY)) {
		ent.type = ST_BINARY;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_STRING;
		Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
		Z_STRLEN_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_CHAR)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_CHAR_CODE) && atts[++i] && atts[i][0]) {
				char tmp_buf[2];

				snprintf(tmp_buf, sizeof(tmp_buf), "%c", (char)strtol(atts[i], NULL, 16));
				php_wddx_process_data(user_data, tmp_buf, strlen(tmp_buf));
				break;
			}
		}
	} else if (!strcmp(name, EL_NUMBER)) {
		ent.type = ST_NUMBER;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_LONG;
		Z_LVAL_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_BOOLEAN)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_VALUE) && atts[++i] && atts[i][0]) {
				ent.type = ST_BOOLEAN;
				SET_STACK_VARNAME;

				ALLOC_ZVAL(ent.data);
				INIT_PZVAL(ent.data);
				Z_TYPE_P(ent.data) = IS_BOOL;
				wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
				php_wddx_process_data(user_data, atts[i], strlen(atts[i]));
				break;
			}
		}
	} else if (!strcmp(name, EL_NULL)) {
		ent.type = ST_NULL;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		ZVAL_NULL(ent.data);

		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_ARRAY)) {
		ent.type = ST_ARRAY;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		array_init(ent.data);
		INIT_PZVAL(ent.data);
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_STRUCT)) {
		ent.type = ST_STRUCT;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		array_init(ent.data);
		INIT_PZVAL(ent.data);
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_VAR)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
				if (stack->varname) efree(stack->varname);
				stack->varname = estrdup(atts[i]);
				break;
			}
		}
	} else if (!strcmp(name, EL_RECORDSET)) {
		int i;

		ent.type = ST_RECORDSET;
		SET_STACK_VARNAME;
		MAKE_STD_ZVAL(ent.data);
		array_init(ent.data);

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], "fieldNames") && atts[++i] && atts[i][0]) {
				zval *tmp;
				char *key;
				char *p1, *p2, *endp;

				endp = (char *)atts[i] + strlen(atts[i]);
				p1 = (char *)atts[i];
				while ((p2 = php_memnstr(p1, ",", sizeof(",")-1, endp)) != NULL) {
					key = estrndup(p1, p2 - p1);
					MAKE_STD_ZVAL(tmp);
					array_init(tmp);
					add_assoc_zval_ex(ent.data, key, p2 - p1 + 1, tmp);
					p1 = p2 + sizeof(",")-1;
					efree(key);
				}

				if (p1 <= endp) {
					MAKE_STD_ZVAL(tmp);
					array_init(tmp);
					add_assoc_zval_ex(ent.data, p1, endp - p1 + 1, tmp);
				}

				break;
			}
		}

		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_FIELD)) {
		int i;
		st_entry ent;

		ent.type = ST_FIELD;
		ent.varname = NULL;
		ent.data = NULL;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
				st_entry *recordset;
				zval **field;

				if (wddx_stack_top(stack, (void**)&recordset) == SUCCESS &&
					recordset->type == ST_RECORDSET &&
					zend_hash_find(Z_ARRVAL_P(recordset->data), (char*)atts[i], strlen(atts[i])+1, (void**)&field) == SUCCESS) {
					ent.data = *field;
				}

				break;
			}
		}

		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_DATETIME)) {
		ent.type = ST_DATETIME;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_LONG;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	}

The php_wddx_push_element function in ext/wddx/wddx.c in PHP before 5.6.26 and 7.x before 7.0.11 allows remote attackers to cause a denial of service (invalid pointer access and out-of-bounds read) or possibly have unspecified other impact via an incorrect boolean element in a wddxPacket XML document, leading to mishandling in a wddx_deserialize call.


 */
static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
{
	st_entry ent;
	wddx_stack *stack = (wddx_stack *)user_data;

	if (!strcmp(name, EL_PACKET)) {
		int i;

		if (atts) for (i=0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_VERSION)) {
				/* nothing for now */
			}
		}
	} else if (!strcmp(name, EL_STRING)) {
		ent.type = ST_STRING;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_STRING;
		Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
		Z_STRLEN_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_BINARY)) {
		ent.type = ST_BINARY;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_STRING;
		Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
		Z_STRLEN_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_CHAR)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_CHAR_CODE) && atts[i+1] && atts[i+1][0]) {
				char tmp_buf[2];

				snprintf(tmp_buf, sizeof(tmp_buf), "%c", (char)strtol(atts[i+1], NULL, 16));
				php_wddx_process_data(user_data, tmp_buf, strlen(tmp_buf));
				break;
			}
		}
	} else if (!strcmp(name, EL_NUMBER)) {
		ent.type = ST_NUMBER;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_LONG;
		Z_LVAL_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_BOOLEAN)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_VALUE) && atts[i+1] && atts[i+1][0]) {
				ent.type = ST_BOOLEAN;
				SET_STACK_VARNAME;

				ALLOC_ZVAL(ent.data);
				INIT_PZVAL(ent.data);
				Z_TYPE_P(ent.data) = IS_BOOL;
				wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
				php_wddx_process_data(user_data, atts[i+1], strlen(atts[i+1]));
				break;
			}
		}
	} else if (!strcmp(name, EL_NULL)) {
		ent.type = ST_NULL;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		ZVAL_NULL(ent.data);

		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_ARRAY)) {
		ent.type = ST_ARRAY;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		array_init(ent.data);
		INIT_PZVAL(ent.data);
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_STRUCT)) {
		ent.type = ST_STRUCT;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		array_init(ent.data);
		INIT_PZVAL(ent.data);
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_VAR)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_NAME) && atts[i+1] && atts[i+1][0]) {
				if (stack->varname) efree(stack->varname);
				stack->varname = estrdup(atts[i+1]);
				break;
			}
		}
	} else if (!strcmp(name, EL_RECORDSET)) {
		int i;

		ent.type = ST_RECORDSET;
		SET_STACK_VARNAME;
		MAKE_STD_ZVAL(ent.data);
		array_init(ent.data);

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], "fieldNames") && atts[i+1] && atts[i+1][0]) {
				zval *tmp;
				char *key;
				char *p1, *p2, *endp;

				i++;
				endp = (char *)atts[i] + strlen(atts[i]);
				p1 = (char *)atts[i];
				while ((p2 = php_memnstr(p1, ",", sizeof(",")-1, endp)) != NULL) {
					key = estrndup(p1, p2 - p1);
					MAKE_STD_ZVAL(tmp);
					array_init(tmp);
					add_assoc_zval_ex(ent.data, key, p2 - p1 + 1, tmp);
					p1 = p2 + sizeof(",")-1;
					efree(key);
				}

				if (p1 <= endp) {
					MAKE_STD_ZVAL(tmp);
					array_init(tmp);
					add_assoc_zval_ex(ent.data, p1, endp - p1 + 1, tmp);
				}

				break;
			}
		}

		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_FIELD)) {
		int i;
		st_entry ent;

		ent.type = ST_FIELD;
		ent.varname = NULL;
		ent.data = NULL;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_NAME) && atts[i+1] && atts[i+1][0]) {
				st_entry *recordset;
				zval **field;

				if (wddx_stack_top(stack, (void**)&recordset) == SUCCESS &&
					recordset->type == ST_RECORDSET &&
					zend_hash_find(Z_ARRVAL_P(recordset->data), (char*)atts[i+1], strlen(atts[i+1])+1, (void**)&field) == SUCCESS) {
					ent.data = *field;
				}

				break;
			}
		}

		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	} else if (!strcmp(name, EL_DATETIME)) {
		ent.type = ST_DATETIME;
		SET_STACK_VARNAME;

		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_LONG;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
	}


libclamav/pe_icons.c: introduce LOGPARSEICONDETAILS define to reduce parseicon logging in default build

static int parseicon(icon_groupset *set, uint32_t rva, cli_ctx *ctx, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size) {
    struct {
	unsigned int sz;
	unsigned int w;
	unsigned int h;
	unsigned short planes;
	unsigned short depth;
	unsigned int comp;
	unsigned int imagesize;
	unsigned int dpix;
	unsigned int dpiy;
	unsigned int used;
	unsigned int important;
    } bmphdr;
    struct icomtr metrics;
    unsigned char *rawimage;
    const char *tempd;
    uint32_t *palette = NULL, *imagedata;
    unsigned int scanlinesz, andlinesz;
    unsigned int width, height, depth, x, y;
    unsigned int err, scalemode = 2, enginesize;
    fmap_t *map;
    uint32_t icoff;
    struct icon_matcher *matcher;
    unsigned int special_32_is_32 = 0;

    if(!ctx || !ctx->engine || !(matcher=ctx->engine->iconcheck))
	return CL_SUCCESS;
    map = *ctx->fmap;
    tempd = (cli_debug_flag && ctx->engine->keeptmp) ? (ctx->engine->tmpdir ? ctx->engine->tmpdir : cli_gettmpdir()) : NULL;
    icoff = cli_rawaddr(rva, exe_sections, nsections, &err, map->len, hdr_size);

    /* read the bitmap header */
    if(err || !(imagedata = fmap_need_off_once(map, icoff, 4))) {
	cli_dbgmsg("parseicon: offset to icon is out of file\n");
	return CL_SUCCESS;
    }

    rva = cli_readint32(imagedata);
    icoff = cli_rawaddr(rva, exe_sections, nsections, &err, map->len, hdr_size);
    if(err || fmap_readn(map, &bmphdr, icoff, sizeof(bmphdr)) != sizeof(bmphdr)) {
	cli_dbgmsg("parseicon: bmp header is out of file\n");
	return CL_SUCCESS;
    }

    if(READ32(bmphdr.sz) < sizeof(bmphdr)) {
	cli_dbgmsg("parseicon: BMP header too small\n");
	return CL_SUCCESS;
    }

    /* seek to the end of v4/v5 header */
    icoff += READ32(bmphdr.sz);

    width = READ32(bmphdr.w);
    height = READ32(bmphdr.h) / 2;
    depth = READ16(bmphdr.depth);
    if(width > 256 || height > 256 || width < 16 || height < 16) {
	cli_dbgmsg("parseicon: Image too small or too big (%ux%u)\n", width, height);
	return CL_SUCCESS;
    }
    if(width < height * 3 / 4 || height < width * 3 / 4) {
	cli_dbgmsg("parseicon: Image not square enough (%ux%u)\n", width, height);
	return CL_SUCCESS;
    }

    /* scaling logic */
    if(width == height) {
	if(width == 16 || width == 24 || width == 32)
	    scalemode = 0;
	else if(!(width % 32) || !(width % 24))
	    scalemode = 1;
	else
	    scalemode = 2;
    }

    cli_dbgmsg("parseicon: Bitmap - %ux%ux%u\n", width, height, depth);

    /* check color depth and load palette */
    switch(depth) {
    default:
    case 0:
	/* PNG OR JPEG */
	cli_dbgmsg("parseicon: PNG icons are not yet sported\n");
	return CL_SUCCESS;
    case 1:
    case 4:
    case 8:
	/* HAVE PALETTE */
	if(!(palette = fmap_need_off(map, icoff, (1<<depth) * sizeof(int))))
	    return CL_SUCCESS;
	icoff += (1<<depth) * sizeof(int);
	/* for(j=0; j<pcolcnt; j++) */
	/* cli_dbgmsg("Palette[%u] = %08x\n", j, palette[j]); */
	break;
    case 16:
    case 24:
    case 32:
	/* NO PALETTE */
	break;
    }

    /* compute line sizes */
    scanlinesz = 4*(width * depth / 32) + 4*(width * depth % 32 != 0);
    andlinesz = ((depth & 0x1f) != 0) * (4*(width / 32) + 4*(width % 32 != 0));

    /* read the raw image */

    if(!(rawimage = fmap_need_off_once(map, icoff, height * (scanlinesz + andlinesz)))) {
	if(palette) fmap_unneed_ptr(map, palette, (1<<depth) * sizeof(int));
	return CL_SUCCESS;
    }
    if(!(imagedata = cli_malloc(width * height * sizeof(*imagedata)))) {
	if(palette) fmap_unneed_ptr(map, palette, (1<<depth) * sizeof(int));
	return CL_SUCCESS;
    }

    /* decode the image to an RGBA array */
    for(y=0; y<height; y++) {
	unsigned int x_off = y * scanlinesz;
	switch(depth) {
	case 1:
	case 4:
	case 8: {
	    unsigned int have = 0;
	    unsigned char c;
	    for(x=0; x<width; x++) {
		if(!have) {
		    c = rawimage[x_off++];
		    have = 8;
		}
		have -= depth;
		imagedata[(height - 1 - y) * width + x] = READ32(palette[(c >> have) & ((1<<depth)-1)]);
	    }
	    break;
	}
	case 16: {
	    for(x=0; x<width; x++) {
		unsigned int b = (rawimage[x_off] & 0x1f);
		unsigned int g = ((rawimage[x_off] >> 5) | ((rawimage[x_off+1] & 0x3)<<3));
		unsigned int r = (rawimage[x_off+1] & 0xfc);
		b = (b<<3) | (b>>2);
		g = ((g<<3) | (g>>2))<<11;
		r = ((r<<3) | (r>>2))<<17;
		imagedata[(height - 1 - y) * width + x] = r | g | b;
		x_off+=2;
	    }
	    break;
	}
	case 24:
	    for(x=0; x<width; x++) {
		unsigned int c = rawimage[x_off] | (rawimage[x_off+1]<<8) | (rawimage[x_off+2]<<16);
		imagedata[(height - 1 - y) * width + x] = c;
		x_off+=3;
	    }
	    break;
	case 32:
	    for(x=0; x<width; x++) {
		unsigned int a = rawimage[x_off + 3] << 24;
		imagedata[(height - 1 - y) * width + x] = rawimage[x_off] | (rawimage[x_off + 1] << 8) | (rawimage[x_off + 2] << 16) | a;
		special_32_is_32 |= a;
		x_off+=4;
	    }
	    break;
	}
    }

    if(palette) fmap_unneed_ptr(map, palette, (1<<depth) * sizeof(int));
    makebmp("0-noalpha", tempd, width, height, imagedata);

    if(depth == 32 && !special_32_is_32) { /* Sometimes it really is 24. Exploited live - see sample 0013839101 */
	andlinesz = 4*(width / 32) + 4*(width % 32 != 0);
	if(!(rawimage = fmap_need_off_once(map, icoff + height * scanlinesz, height * andlinesz))) {
	    /* Likely a broken sample - 32bit icon with 24bit data and a broken mask:
	       i could really break out here but i've got the full image, so i'm just forcing full alpha
	       Found in samples: 0008777448, 0009116157, 0009116157 */
	    for(y=0; y<height; y++)
		for(x=0; x<width; x++)
		    imagedata[y * width + x] |= 0xff000000;
	    special_32_is_32 = 1;
	    cli_dbgmsg("parseicon: found a broken and stupid icon\n");
	} else cli_dbgmsg("parseicon: found a stupid icon\n");
    } else rawimage += height * scanlinesz;

    /* Set alpha on or off based on the mask */
    if((depth & 0x1f) || !special_32_is_32) {
	for(y=0; y<height; y++) {
	    unsigned int x_off = y * andlinesz;
	    unsigned int have = 0;
	    unsigned char c;
	    for(x=0; x<width; x++) {
		if(!have) {
		    c = rawimage[x_off++];
		    have = 8;
		}
		have--;
		imagedata[(height - 1 - y) * width + x] |= (!((c >> have) & 1)) * 0xff000000;
	    }
	}
    }
    makebmp("1-alpha-mask", tempd, width, height, imagedata);

    /* Blend alpha */
    for(y=0; y<height; y++) {
	for(x=0; x<width; x++) {
	    unsigned int r, g, b, a;
	    unsigned int c = imagedata[y * width + x];
	    a = c >> 24;
	    r = (c >> 16) & 0xff;
	    g = (c >> 8) & 0xff;
	    b = c & 0xff;
	    r = 0xff - a + a * r / 0xff;
	    g = 0xff - a + a * g / 0xff;
	    b = 0xff - a + a * b / 0xff;
	    imagedata[y * width + x] = 0xff000000 | (r << 16) | (g << 8) | b;
	}
    }

    switch (scalemode) {
    case 0:
	break;
    case 1:
	/* Fast 50% scaler with linear gamma */
	while(width > 32) {
	    for(y=0; y<height; y+=2) {
		for(x=0; x<width; x+=2) {
		    unsigned int c1 = imagedata[y * width + x], c2 =imagedata[y * width + x + 1], c3 = imagedata[(y+1) * width + x], c4 = imagedata[(y+1) * width + x + 1];
		    c1 = (((c1 ^ c2) & 0xfefefefe)>>1) + (c1 & c2);
		    c2 = (((c3 ^ c4) & 0xfefefefe)>>1) + (c3 & c4);
		    imagedata[y/2 * width/2 + x/2] = (((c1 ^ c2) & 0xfefefefe)>>1) + (c1 & c2);
		}
	    }
	    width /= 2;
	    height /= 2;
	    cli_dbgmsg("parseicon: Fast scaling to %ux%u\n", width, height);
	}
	break;
    case 2:
	/* Slow up/down scale */
	{
	    double scalex, scaley;
	    unsigned int newsize;
	    uint32_t *newdata;

	    if(abs((int)width - 32) + abs((int)height - 32) < abs((int)width - 24) + abs((int)height - 24))
		newsize = 32;
	    else if(abs((int)width - 24) + abs((int)height - 24) < abs((int)width - 16) + abs((int)height - 16))
		newsize = 24;
	    else
		newsize = 16;
	    scalex = (double)width / newsize;
	    scaley = (double)height / newsize;
	    if(!(newdata = cli_malloc(newsize * newsize * sizeof(*newdata)))) {
		return CL_SUCCESS;
	    }
	    cli_dbgmsg("parseicon: Slow scaling to %ux%u (%f, %f)\n", newsize, newsize, scalex, scaley);
	    for(y = 0; y<newsize; y++) {
		unsigned int oldy = (unsigned int)(y * scaley) * width;
		for(x = 0; x<newsize; x++)
		    newdata[y*newsize + x] = imagedata[oldy + (unsigned int)(x * scalex + 0.5f)];
	    }
	    free(imagedata);
	    height = newsize;
	    width = newsize;
	    imagedata = newdata;
	}
    }
    makebmp("2-alpha-blend", tempd, width, height, imagedata);

    getmetrics(width, imagedata, &metrics, tempd);
    free(imagedata);

    enginesize = (width >> 3) - 2;
    for(x=0; x<matcher->icon_counts[enginesize]; x++) {
	unsigned int color = 0, gray = 0, bright, dark, edge, noedge, reds, greens, blues, ccount;
	unsigned int colors, confidence, bwmatch = 0, positivematch = 64 + 4*(2-enginesize);
	unsigned int i, j;

	i = matcher->icons[enginesize][x].group[0];
	j = i % 64;
	i /= 64;
	if(!(set->v[0][i] & ((uint64_t)1<<j))) continue;
	i = matcher->icons[enginesize][x].group[1];
	j = i % 64;
	i /= 64;
	if(!(set->v[1][i] & ((uint64_t)1<<j))) continue;

	if(!metrics.ccount && !matcher->icons[enginesize][x].ccount) {
	    /* BW matching */
	    edge = matchbwpoint(width, metrics.edge_x, metrics.edge_y, metrics.edge_avg, metrics.color_x, metrics.color_y, metrics.color_avg, matcher->icons[enginesize][x].edge_x, matcher->icons[enginesize][x].edge_y, matcher->icons[enginesize][x].edge_avg, matcher->icons[enginesize][x].color_x, matcher->icons[enginesize][x].color_y, matcher->icons[enginesize][x].color_avg);
	    noedge = matchbwpoint(width, metrics.noedge_x, metrics.noedge_y, metrics.noedge_avg, metrics.gray_x, metrics.gray_y, metrics.gray_avg, matcher->icons[enginesize][x].noedge_x, matcher->icons[enginesize][x].noedge_y, matcher->icons[enginesize][x].noedge_avg, matcher->icons[enginesize][x].gray_x, matcher->icons[enginesize][x].gray_y, matcher->icons[enginesize][x].gray_avg);
	    bwmatch = 1;
	} else {
	    edge = matchpoint(width, metrics.edge_x, metrics.edge_y, metrics.edge_avg, matcher->icons[enginesize][x].edge_x, matcher->icons[enginesize][x].edge_y, matcher->icons[enginesize][x].edge_avg, 255);
	    noedge = matchpoint(width, metrics.noedge_x, metrics.noedge_y, metrics.noedge_avg, matcher->icons[enginesize][x].noedge_x, matcher->icons[enginesize][x].noedge_y, matcher->icons[enginesize][x].noedge_avg, 255);
	    if(metrics.ccount && matcher->icons[enginesize][x].ccount) {
		/* color matching */
		color = matchpoint(width, metrics.color_x, metrics.color_y, metrics.color_avg, matcher->icons[enginesize][x].color_x, matcher->icons[enginesize][x].color_y, matcher->icons[enginesize][x].color_avg, 4072);
		gray = matchpoint(width, metrics.gray_x, metrics.gray_y, metrics.gray_avg, matcher->icons[enginesize][x].gray_x, matcher->icons[enginesize][x].gray_y, matcher->icons[enginesize][x].gray_avg, 4072);
	    }
	}

	bright = matchpoint(width, metrics.bright_x, metrics.bright_y, metrics.bright_avg, matcher->icons[enginesize][x].bright_x, matcher->icons[enginesize][x].bright_y, matcher->icons[enginesize][x].bright_avg, 255);
	dark = matchpoint(width, metrics.dark_x, metrics.dark_y, metrics.dark_avg, matcher->icons[enginesize][x].dark_x, matcher->icons[enginesize][x].dark_y, matcher->icons[enginesize][x].dark_avg, 255);

	reds = abs((int)metrics.rsum - (int)matcher->icons[enginesize][x].rsum) * 10;
	reds = (reds < 100) * (100 - reds);
	greens = abs((int)metrics.gsum - (int)matcher->icons[enginesize][x].gsum) * 10;
	greens = (greens < 100) * (100 - greens);
	blues = abs((int)metrics.bsum - (int)matcher->icons[enginesize][x].bsum) * 10;
	blues = (blues < 100) * (100 - blues);
	ccount = abs((int)metrics.ccount - (int)matcher->icons[enginesize][x].ccount) * 10;
	ccount = (ccount < 100) * (100 - ccount);
	colors = (reds + greens + blues + ccount) / 4;

	if(bwmatch) {
	    confidence = (bright + dark + edge * 2 + noedge) / 6;
	    positivematch = 70;
	} else
	    confidence = (color + (gray + bright + noedge)*2/3 + dark + edge + colors) / 6;

	cli_dbgmsg("parseicon: edge confidence: %u%%\n", edge);
	cli_dbgmsg("parseicon: noedge confidence: %u%%\n", noedge);
	if(!bwmatch) {
	    cli_dbgmsg("parseicon: color confidence: %u%%\n", color);
	    cli_dbgmsg("parseicon: gray confidence: %u%%\n", gray);
	}
	cli_dbgmsg("parseicon: bright confidence: %u%%\n", bright);
	cli_dbgmsg("parseicon: dark confidence: %u%%\n", dark);
	if(!bwmatch)
	    cli_dbgmsg("parseicon: spread confidence: red %u%%, green %u%%, blue %u%% - colors %u%%\n", reds, greens, blues, ccount);

	if(confidence >= positivematch) {
	    cli_dbgmsg("confidence: %u\n", confidence);

	    cli_append_virus(ctx,matcher->icons[enginesize][x].name);
	    return CL_VIRUS;
	}
    }

    return CL_SUCCESS;
}

Integer underflow in the cli_scanpe function in pe.c in ClamAV before 0.97.8 allows remote attackers to cause a denial of service (crash) via a skewed offset larger than the size of the PE section in a UPX packed executable, which triggers an out-of-bounds read.


static int parseicon(icon_groupset *set, uint32_t rva, cli_ctx *ctx, struct cli_exe_section *exe_sections, uint16_t nsections, uint32_t hdr_size) {
    struct {
	unsigned int sz;
	unsigned int w;
	unsigned int h;
	unsigned short planes;
	unsigned short depth;
	unsigned int comp;
	unsigned int imagesize;
	unsigned int dpix;
	unsigned int dpiy;
	unsigned int used;
	unsigned int important;
    } bmphdr;
    struct icomtr metrics;
    unsigned char *rawimage;
    const char *tempd;
    uint32_t *palette = NULL, *imagedata;
    unsigned int scanlinesz, andlinesz;
    unsigned int width, height, depth, x, y;
    unsigned int err, scalemode = 2, enginesize;
    fmap_t *map;
    uint32_t icoff;
    struct icon_matcher *matcher;
    unsigned int special_32_is_32 = 0;

    if(!ctx || !ctx->engine || !(matcher=ctx->engine->iconcheck))
	return CL_SUCCESS;
    map = *ctx->fmap;
    tempd = (cli_debug_flag && ctx->engine->keeptmp) ? (ctx->engine->tmpdir ? ctx->engine->tmpdir : cli_gettmpdir()) : NULL;
    icoff = cli_rawaddr(rva, exe_sections, nsections, &err, map->len, hdr_size);

    /* read the bitmap header */
    if(err || !(imagedata = fmap_need_off_once(map, icoff, 4))) {
	cli_dbgmsg("parseicon: offset to icon is out of file\n");
	return CL_SUCCESS;
    }

    rva = cli_readint32(imagedata);
    icoff = cli_rawaddr(rva, exe_sections, nsections, &err, map->len, hdr_size);
    if(err || fmap_readn(map, &bmphdr, icoff, sizeof(bmphdr)) != sizeof(bmphdr)) {
	cli_dbgmsg("parseicon: bmp header is out of file\n");
	return CL_SUCCESS;
    }

    if(READ32(bmphdr.sz) < sizeof(bmphdr)) {
	cli_dbgmsg("parseicon: BMP header too small\n");
	return CL_SUCCESS;
    }

    /* seek to the end of v4/v5 header */
    icoff += READ32(bmphdr.sz);

    width = READ32(bmphdr.w);
    height = READ32(bmphdr.h) / 2;
    depth = READ16(bmphdr.depth);
    if(width > 256 || height > 256 || width < 16 || height < 16) {
	cli_dbgmsg("parseicon: Image too small or too big (%ux%u)\n", width, height);
	return CL_SUCCESS;
    }
    if(width < height * 3 / 4 || height < width * 3 / 4) {
	cli_dbgmsg("parseicon: Image not square enough (%ux%u)\n", width, height);
	return CL_SUCCESS;
    }

    /* scaling logic */
    if(width == height) {
	if(width == 16 || width == 24 || width == 32)
	    scalemode = 0;
	else if(!(width % 32) || !(width % 24))
	    scalemode = 1;
	else
	    scalemode = 2;
    }

    cli_dbgmsg("parseicon: Bitmap - %ux%ux%u\n", width, height, depth);

    /* check color depth and load palette */
    switch(depth) {
    default:
    case 0:
	/* PNG OR JPEG */
	cli_dbgmsg("parseicon: PNG icons are not yet sported\n");
	return CL_SUCCESS;
    case 1:
    case 4:
    case 8:
	/* HAVE PALETTE */
	if(!(palette = fmap_need_off(map, icoff, (1<<depth) * sizeof(int))))
	    return CL_SUCCESS;
	icoff += (1<<depth) * sizeof(int);
	/* for(j=0; j<pcolcnt; j++) */
	/* cli_dbgmsg("Palette[%u] = %08x\n", j, palette[j]); */
	break;
    case 16:
    case 24:
    case 32:
	/* NO PALETTE */
	break;
    }

    /* compute line sizes */
    scanlinesz = 4*(width * depth / 32) + 4*(width * depth % 32 != 0);
    andlinesz = ((depth & 0x1f) != 0) * (4*(width / 32) + 4*(width % 32 != 0));

    /* read the raw image */

    if(!(rawimage = fmap_need_off_once(map, icoff, height * (scanlinesz + andlinesz)))) {
	if(palette) fmap_unneed_ptr(map, palette, (1<<depth) * sizeof(int));
	return CL_SUCCESS;
    }
    if(!(imagedata = cli_malloc(width * height * sizeof(*imagedata)))) {
	if(palette) fmap_unneed_ptr(map, palette, (1<<depth) * sizeof(int));
	return CL_SUCCESS;
    }

    /* decode the image to an RGBA array */
    for(y=0; y<height; y++) {
	unsigned int x_off = y * scanlinesz;
	switch(depth) {
	case 1:
	case 4:
	case 8: {
	    unsigned int have = 0;
	    unsigned char c;
	    for(x=0; x<width; x++) {
		if(!have) {
		    c = rawimage[x_off++];
		    have = 8;
		}
		have -= depth;
		imagedata[(height - 1 - y) * width + x] = READ32(palette[(c >> have) & ((1<<depth)-1)]);
	    }
	    break;
	}
	case 16: {
	    for(x=0; x<width; x++) {
		unsigned int b = (rawimage[x_off] & 0x1f);
		unsigned int g = ((rawimage[x_off] >> 5) | ((rawimage[x_off+1] & 0x3)<<3));
		unsigned int r = (rawimage[x_off+1] & 0xfc);
		b = (b<<3) | (b>>2);
		g = ((g<<3) | (g>>2))<<11;
		r = ((r<<3) | (r>>2))<<17;
		imagedata[(height - 1 - y) * width + x] = r | g | b;
		x_off+=2;
	    }
	    break;
	}
	case 24:
	    for(x=0; x<width; x++) {
		unsigned int c = rawimage[x_off] | (rawimage[x_off+1]<<8) | (rawimage[x_off+2]<<16);
		imagedata[(height - 1 - y) * width + x] = c;
		x_off+=3;
	    }
	    break;
	case 32:
	    for(x=0; x<width; x++) {
		unsigned int a = rawimage[x_off + 3] << 24;
		imagedata[(height - 1 - y) * width + x] = rawimage[x_off] | (rawimage[x_off + 1] << 8) | (rawimage[x_off + 2] << 16) | a;
		special_32_is_32 |= a;
		x_off+=4;
	    }
	    break;
	}
    }

    if(palette) fmap_unneed_ptr(map, palette, (1<<depth) * sizeof(int));
    makebmp("0-noalpha", tempd, width, height, imagedata);

    if(depth == 32 && !special_32_is_32) { /* Sometimes it really is 24. Exploited live - see sample 0013839101 */
	andlinesz = 4*(width / 32) + 4*(width % 32 != 0);
	if(!(rawimage = fmap_need_off_once(map, icoff + height * scanlinesz, height * andlinesz))) {
	    /* Likely a broken sample - 32bit icon with 24bit data and a broken mask:
	       i could really break out here but i've got the full image, so i'm just forcing full alpha
	       Found in samples: 0008777448, 0009116157, 0009116157 */
	    for(y=0; y<height; y++)
		for(x=0; x<width; x++)
		    imagedata[y * width + x] |= 0xff000000;
	    special_32_is_32 = 1;
	    cli_dbgmsg("parseicon: found a broken and stupid icon\n");
	} else cli_dbgmsg("parseicon: found a stupid icon\n");
    } else rawimage += height * scanlinesz;

    /* Set alpha on or off based on the mask */
    if((depth & 0x1f) || !special_32_is_32) {
	for(y=0; y<height; y++) {
	    unsigned int x_off = y * andlinesz;
	    unsigned int have = 0;
	    unsigned char c;
	    for(x=0; x<width; x++) {
		if(!have) {
		    c = rawimage[x_off++];
		    have = 8;
		}
		have--;
		imagedata[(height - 1 - y) * width + x] |= (!((c >> have) & 1)) * 0xff000000;
	    }
	}
    }
    makebmp("1-alpha-mask", tempd, width, height, imagedata);

    /* Blend alpha */
    for(y=0; y<height; y++) {
	for(x=0; x<width; x++) {
	    unsigned int r, g, b, a;
	    unsigned int c = imagedata[y * width + x];
	    a = c >> 24;
	    r = (c >> 16) & 0xff;
	    g = (c >> 8) & 0xff;
	    b = c & 0xff;
	    r = 0xff - a + a * r / 0xff;
	    g = 0xff - a + a * g / 0xff;
	    b = 0xff - a + a * b / 0xff;
	    imagedata[y * width + x] = 0xff000000 | (r << 16) | (g << 8) | b;
	}
    }

    switch (scalemode) {
    case 0:
	break;
    case 1:
	/* Fast 50% scaler with linear gamma */
	while(width > 32) {
	    for(y=0; y<height; y+=2) {
		for(x=0; x<width; x+=2) {
		    unsigned int c1 = imagedata[y * width + x], c2 =imagedata[y * width + x + 1], c3 = imagedata[(y+1) * width + x], c4 = imagedata[(y+1) * width + x + 1];
		    c1 = (((c1 ^ c2) & 0xfefefefe)>>1) + (c1 & c2);
		    c2 = (((c3 ^ c4) & 0xfefefefe)>>1) + (c3 & c4);
		    imagedata[y/2 * width/2 + x/2] = (((c1 ^ c2) & 0xfefefefe)>>1) + (c1 & c2);
		}
	    }
	    width /= 2;
	    height /= 2;
	    cli_dbgmsg("parseicon: Fast scaling to %ux%u\n", width, height);
	}
	break;
    case 2:
	/* Slow up/down scale */
	{
	    double scalex, scaley;
	    unsigned int newsize;
	    uint32_t *newdata;

	    if(abs((int)width - 32) + abs((int)height - 32) < abs((int)width - 24) + abs((int)height - 24))
		newsize = 32;
	    else if(abs((int)width - 24) + abs((int)height - 24) < abs((int)width - 16) + abs((int)height - 16))
		newsize = 24;
	    else
		newsize = 16;
	    scalex = (double)width / newsize;
	    scaley = (double)height / newsize;
	    if(!(newdata = cli_malloc(newsize * newsize * sizeof(*newdata)))) {
		return CL_SUCCESS;
	    }
	    cli_dbgmsg("parseicon: Slow scaling to %ux%u (%f, %f)\n", newsize, newsize, scalex, scaley);
	    for(y = 0; y<newsize; y++) {
		unsigned int oldy = (unsigned int)(y * scaley) * width;
		for(x = 0; x<newsize; x++)
		    newdata[y*newsize + x] = imagedata[oldy + (unsigned int)(x * scalex + 0.5f)];
	    }
	    free(imagedata);
	    height = newsize;
	    width = newsize;
	    imagedata = newdata;
	}
    }
    makebmp("2-alpha-blend", tempd, width, height, imagedata);

    getmetrics(width, imagedata, &metrics, tempd);
    free(imagedata);

    enginesize = (width >> 3) - 2;
    for(x=0; x<matcher->icon_counts[enginesize]; x++) {
	unsigned int color = 0, gray = 0, bright, dark, edge, noedge, reds, greens, blues, ccount;
	unsigned int colors, confidence, bwmatch = 0, positivematch = 64 + 4*(2-enginesize);
	unsigned int i, j;

	i = matcher->icons[enginesize][x].group[0];
	j = i % 64;
	i /= 64;
	if(!(set->v[0][i] & ((uint64_t)1<<j))) continue;
	i = matcher->icons[enginesize][x].group[1];
	j = i % 64;
	i /= 64;
	if(!(set->v[1][i] & ((uint64_t)1<<j))) continue;

	if(!metrics.ccount && !matcher->icons[enginesize][x].ccount) {
	    /* BW matching */
	    edge = matchbwpoint(width, metrics.edge_x, metrics.edge_y, metrics.edge_avg, metrics.color_x, metrics.color_y, metrics.color_avg, matcher->icons[enginesize][x].edge_x, matcher->icons[enginesize][x].edge_y, matcher->icons[enginesize][x].edge_avg, matcher->icons[enginesize][x].color_x, matcher->icons[enginesize][x].color_y, matcher->icons[enginesize][x].color_avg);
	    noedge = matchbwpoint(width, metrics.noedge_x, metrics.noedge_y, metrics.noedge_avg, metrics.gray_x, metrics.gray_y, metrics.gray_avg, matcher->icons[enginesize][x].noedge_x, matcher->icons[enginesize][x].noedge_y, matcher->icons[enginesize][x].noedge_avg, matcher->icons[enginesize][x].gray_x, matcher->icons[enginesize][x].gray_y, matcher->icons[enginesize][x].gray_avg);
	    bwmatch = 1;
	} else {
	    edge = matchpoint(width, metrics.edge_x, metrics.edge_y, metrics.edge_avg, matcher->icons[enginesize][x].edge_x, matcher->icons[enginesize][x].edge_y, matcher->icons[enginesize][x].edge_avg, 255);
	    noedge = matchpoint(width, metrics.noedge_x, metrics.noedge_y, metrics.noedge_avg, matcher->icons[enginesize][x].noedge_x, matcher->icons[enginesize][x].noedge_y, matcher->icons[enginesize][x].noedge_avg, 255);
	    if(metrics.ccount && matcher->icons[enginesize][x].ccount) {
		/* color matching */
		color = matchpoint(width, metrics.color_x, metrics.color_y, metrics.color_avg, matcher->icons[enginesize][x].color_x, matcher->icons[enginesize][x].color_y, matcher->icons[enginesize][x].color_avg, 4072);
		gray = matchpoint(width, metrics.gray_x, metrics.gray_y, metrics.gray_avg, matcher->icons[enginesize][x].gray_x, matcher->icons[enginesize][x].gray_y, matcher->icons[enginesize][x].gray_avg, 4072);
	    }
	}

	bright = matchpoint(width, metrics.bright_x, metrics.bright_y, metrics.bright_avg, matcher->icons[enginesize][x].bright_x, matcher->icons[enginesize][x].bright_y, matcher->icons[enginesize][x].bright_avg, 255);
	dark = matchpoint(width, metrics.dark_x, metrics.dark_y, metrics.dark_avg, matcher->icons[enginesize][x].dark_x, matcher->icons[enginesize][x].dark_y, matcher->icons[enginesize][x].dark_avg, 255);

	reds = abs((int)metrics.rsum - (int)matcher->icons[enginesize][x].rsum) * 10;
	reds = (reds < 100) * (100 - reds);
	greens = abs((int)metrics.gsum - (int)matcher->icons[enginesize][x].gsum) * 10;
	greens = (greens < 100) * (100 - greens);
	blues = abs((int)metrics.bsum - (int)matcher->icons[enginesize][x].bsum) * 10;
	blues = (blues < 100) * (100 - blues);
	ccount = abs((int)metrics.ccount - (int)matcher->icons[enginesize][x].ccount) * 10;
	ccount = (ccount < 100) * (100 - ccount);
	colors = (reds + greens + blues + ccount) / 4;

	if(bwmatch) {
	    confidence = (bright + dark + edge * 2 + noedge) / 6;
	    positivematch = 70;
	} else
	    confidence = (color + (gray + bright + noedge)*2/3 + dark + edge + colors) / 6;

#ifdef LOGPARSEICONDETAILS
	cli_dbgmsg("parseicon: edge confidence: %u%%\n", edge);
	cli_dbgmsg("parseicon: noedge confidence: %u%%\n", noedge);
	if(!bwmatch) {
	    cli_dbgmsg("parseicon: color confidence: %u%%\n", color);
	    cli_dbgmsg("parseicon: gray confidence: %u%%\n", gray);
	}
	cli_dbgmsg("parseicon: bright confidence: %u%%\n", bright);
	cli_dbgmsg("parseicon: dark confidence: %u%%\n", dark);
	if(!bwmatch)
	    cli_dbgmsg("parseicon: spread confidence: red %u%%, green %u%%, blue %u%% - colors %u%%\n", reds, greens, blues, ccount);
#endif

	if(confidence >= positivematch) {
	    cli_dbgmsg("confidence: %u\n", confidence);

	    cli_append_virus(ctx,matcher->icons[enginesize][x].name);
	    return CL_VIRUS;
	}
    }

    return CL_SUCCESS;
}


Fix #78863: DirectoryIterator class silently truncates after a null byte

Since the constructor of DirectoryIterator and friends is supposed to
accepts paths (i.e. strings without NUL bytes), we must not accept
arbitrary strings.

void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long ctor_flags) /* {{{ */
{
	spl_filesystem_object *intern;
	char *path;
	int parsed;
	size_t len;
	zend_long flags;
	zend_error_handling error_handling;

	zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling);

	if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_FLAGS)) {
		flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_FILEINFO;
		parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &path, &len, &flags);
	} else {
		flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_SELF;
		parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "s", &path, &len);
	}
	if (SPL_HAS_FLAG(ctor_flags, SPL_FILE_DIR_SKIPDOTS)) {
		flags |= SPL_FILE_DIR_SKIPDOTS;
	}
	if (SPL_HAS_FLAG(ctor_flags, SPL_FILE_DIR_UNIXPATHS)) {
		flags |= SPL_FILE_DIR_UNIXPATHS;
	}
	if (parsed == FAILURE) {
		zend_restore_error_handling(&error_handling);
		return;
	}
	if (!len) {
		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Directory name must not be empty.");
		zend_restore_error_handling(&error_handling);
		return;
	}

	intern = Z_SPLFILESYSTEM_P(getThis());
	if (intern->_path) {
		/* object is alreay initialized */
		zend_restore_error_handling(&error_handling);
		php_error_docref(NULL, E_WARNING, "Directory object is already initialized");
		return;
	}
	intern->flags = flags;
#ifdef HAVE_GLOB
	if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_GLOB) && strstr(path, "glob://") != path) {
		spprintf(&path, 0, "glob://%s", path);
		spl_filesystem_dir_open(intern, path);
		efree(path);
	} else
#endif
	{
		spl_filesystem_dir_open(intern, path);

	}

	intern->u.dir.is_recursive = instanceof_function(intern->std.ce, spl_ce_RecursiveDirectoryIterator) ? 1 : 0;

	zend_restore_error_handling(&error_handling);
}

In PHP versions 7.2.x below 7.2.26, 7.3.x below 7.3.13 and 7.4.0, PHP DirectoryIterator class accepts filenames with embedded \0 byte and treats them as terminating at that byte. This could lead to security vulnerabilities, e.g. in applications checking paths that the code is allowed to access.


void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long ctor_flags) /* {{{ */
{
	spl_filesystem_object *intern;
	char *path;
	int parsed;
	size_t len;
	zend_long flags;
	zend_error_handling error_handling;

	zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling);

	if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_FLAGS)) {
		flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_FILEINFO;
		parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &path, &len, &flags);
	} else {
		flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_SELF;
		parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "p", &path, &len);
	}
	if (SPL_HAS_FLAG(ctor_flags, SPL_FILE_DIR_SKIPDOTS)) {
		flags |= SPL_FILE_DIR_SKIPDOTS;
	}
	if (SPL_HAS_FLAG(ctor_flags, SPL_FILE_DIR_UNIXPATHS)) {
		flags |= SPL_FILE_DIR_UNIXPATHS;
	}
	if (parsed == FAILURE) {
		zend_restore_error_handling(&error_handling);
		return;
	}
	if (!len) {
		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Directory name must not be empty.");
		zend_restore_error_handling(&error_handling);
		return;
	}

	intern = Z_SPLFILESYSTEM_P(getThis());
	if (intern->_path) {
		/* object is alreay initialized */
		zend_restore_error_handling(&error_handling);
		php_error_docref(NULL, E_WARNING, "Directory object is already initialized");
		return;
	}
	intern->flags = flags;
#ifdef HAVE_GLOB
	if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_GLOB) && strstr(path, "glob://") != path) {
		spprintf(&path, 0, "glob://%s", path);
		spl_filesystem_dir_open(intern, path);
		efree(path);
	} else
#endif
	{
		spl_filesystem_dir_open(intern, path);

	}

	intern->u.dir.is_recursive = instanceof_function(intern->std.ce, spl_ce_RecursiveDirectoryIterator) ? 1 : 0;

	zend_restore_error_handling(&error_handling);
}


GHSL-2021-074: authentication bypass vulnerability in polkit

initial values returned if error caught

polkit_system_bus_name_get_creds_sync (PolkitSystemBusName           *system_bus_name,
				       guint32                       *out_uid,
				       guint32                       *out_pid,
				       GCancellable                  *cancellable,
				       GError                       **error)
{
  gboolean ret = FALSE;
  AsyncGetBusNameCredsData data = { 0, };
  GDBusConnection *connection = NULL;
  GMainContext *tmp_context = NULL;

  connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
  if (connection == NULL)
    goto out;

  data.error = error;

  tmp_context = g_main_context_new ();
  g_main_context_push_thread_default (tmp_context);

  /* Do two async calls as it's basically as fast as one sync call.
   */
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixUser",      /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid,
			  &data);
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixProcessID", /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid,
			  &data);

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
    g_main_context_iteration (tmp_context, TRUE);

  if (out_uid)
    *out_uid = data.uid;
  if (out_pid)
    *out_pid = data.pid;
  ret = TRUE;
 out:
  if (tmp_context)
    {
      g_main_context_pop_thread_default (tmp_context);
      g_main_context_unref (tmp_context);
    }
  if (connection != NULL)
    g_object_unref (connection);
  return ret;
}

It was found that polkit could be tricked into bypassing the credential checks for D-Bus requests, elevating the privileges of the requestor to the root user. This flaw could be used by an unprivileged local attacker to, for example, create a new local administrator. The highest threat from this vulnerability is to data confidentiality and integrity as well as system availability.


polkit_system_bus_name_get_creds_sync (PolkitSystemBusName           *system_bus_name,
				       guint32                       *out_uid,
				       guint32                       *out_pid,
				       GCancellable                  *cancellable,
				       GError                       **error)
{
  gboolean ret = FALSE;
  AsyncGetBusNameCredsData data = { 0, };
  GDBusConnection *connection = NULL;
  GMainContext *tmp_context = NULL;

  connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
  if (connection == NULL)
    goto out;

  data.error = error;

  tmp_context = g_main_context_new ();
  g_main_context_push_thread_default (tmp_context);

  /* Do two async calls as it's basically as fast as one sync call.
   */
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixUser",      /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid,
			  &data);
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixProcessID", /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid,
			  &data);

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
    g_main_context_iteration (tmp_context, TRUE);

  if (data.caught_error)
    goto out;

  if (out_uid)
    *out_uid = data.uid;
  if (out_pid)
    *out_pid = data.pid;
  ret = TRUE;
 out:
  if (tmp_context)
    {
      g_main_context_pop_thread_default (tmp_context);
      g_main_context_unref (tmp_context);
    }
  if (connection != NULL)
    g_object_unref (connection);
  return ret;
}


nl80211: fix memory leak in nl80211_get_ftm_responder_stats

In nl80211_get_ftm_responder_stats, a new skb is created via nlmsg_new
named msg. If nl80211hdr_put() fails, then msg should be released. The
return statement should be replace by goto to error handling code.

Fixes: 81e54d08d9d8 ("cfg80211: support FTM responder configuration/statistics")
Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Link: https://lore.kernel.org/r/20191004194220.19412-1-navid.emamdoost@gmail.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
					   struct genl_info *info)
{
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	struct net_device *dev = info->user_ptr[1];
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_ftm_responder_stats ftm_stats = {};
	struct sk_buff *msg;
	void *hdr;
	struct nlattr *ftm_stats_attr;
	int err;

	if (wdev->iftype != NL80211_IFTYPE_AP || !wdev->beacon_interval)
		return -EOPNOTSUPP;

	err = rdev_get_ftm_responder_stats(rdev, dev, &ftm_stats);
	if (err)
		return err;

	if (!ftm_stats.filled)
		return -ENODATA;

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
			     NL80211_CMD_GET_FTM_RESPONDER_STATS);
	if (!hdr)
		return -ENOBUFS;

	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
		goto nla_put_failure;

	ftm_stats_attr = nla_nest_start_noflag(msg,
					       NL80211_ATTR_FTM_RESPONDER_STATS);
	if (!ftm_stats_attr)
		goto nla_put_failure;

#define SET_FTM(field, name, type)					 \
	do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
	    nla_put_ ## type(msg, NL80211_FTM_STATS_ ## name,		 \
			     ftm_stats.field))				 \
		goto nla_put_failure; } while (0)
#define SET_FTM_U64(field, name)					 \
	do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
	    nla_put_u64_64bit(msg, NL80211_FTM_STATS_ ## name,		 \
			      ftm_stats.field, NL80211_FTM_STATS_PAD))	 \
		goto nla_put_failure; } while (0)

	SET_FTM(success_num, SUCCESS_NUM, u32);
	SET_FTM(partial_num, PARTIAL_NUM, u32);
	SET_FTM(failed_num, FAILED_NUM, u32);
	SET_FTM(asap_num, ASAP_NUM, u32);
	SET_FTM(non_asap_num, NON_ASAP_NUM, u32);
	SET_FTM_U64(total_duration_ms, TOTAL_DURATION_MSEC);
	SET_FTM(unknown_triggers_num, UNKNOWN_TRIGGERS_NUM, u32);
	SET_FTM(reschedule_requests_num, RESCHEDULE_REQUESTS_NUM, u32);
	SET_FTM(out_of_window_triggers_num, OUT_OF_WINDOW_TRIGGERS_NUM, u32);
#undef SET_FTM

	nla_nest_end(msg, ftm_stats_attr);

	genlmsg_end(msg, hdr);
	return genlmsg_reply(msg, info);

nla_put_failure:
	nlmsg_free(msg);
	return -ENOBUFS;
}

A memory leak in the nl80211_get_ftm_responder_stats() function in net/wireless/nl80211.c in the Linux kernel through 5.3.11 allows attackers to cause a denial of service (memory consumption) by triggering nl80211hdr_put() failures, aka CID-1399c59fa929. NOTE: third parties dispute the relevance of this because it occurs on a code path where a successful allocation has already occurred


static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
					   struct genl_info *info)
{
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	struct net_device *dev = info->user_ptr[1];
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_ftm_responder_stats ftm_stats = {};
	struct sk_buff *msg;
	void *hdr;
	struct nlattr *ftm_stats_attr;
	int err;

	if (wdev->iftype != NL80211_IFTYPE_AP || !wdev->beacon_interval)
		return -EOPNOTSUPP;

	err = rdev_get_ftm_responder_stats(rdev, dev, &ftm_stats);
	if (err)
		return err;

	if (!ftm_stats.filled)
		return -ENODATA;

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg)
		return -ENOMEM;

	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
			     NL80211_CMD_GET_FTM_RESPONDER_STATS);
	if (!hdr)
		goto nla_put_failure;

	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
		goto nla_put_failure;

	ftm_stats_attr = nla_nest_start_noflag(msg,
					       NL80211_ATTR_FTM_RESPONDER_STATS);
	if (!ftm_stats_attr)
		goto nla_put_failure;

#define SET_FTM(field, name, type)					 \
	do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
	    nla_put_ ## type(msg, NL80211_FTM_STATS_ ## name,		 \
			     ftm_stats.field))				 \
		goto nla_put_failure; } while (0)
#define SET_FTM_U64(field, name)					 \
	do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
	    nla_put_u64_64bit(msg, NL80211_FTM_STATS_ ## name,		 \
			      ftm_stats.field, NL80211_FTM_STATS_PAD))	 \
		goto nla_put_failure; } while (0)

	SET_FTM(success_num, SUCCESS_NUM, u32);
	SET_FTM(partial_num, PARTIAL_NUM, u32);
	SET_FTM(failed_num, FAILED_NUM, u32);
	SET_FTM(asap_num, ASAP_NUM, u32);
	SET_FTM(non_asap_num, NON_ASAP_NUM, u32);
	SET_FTM_U64(total_duration_ms, TOTAL_DURATION_MSEC);
	SET_FTM(unknown_triggers_num, UNKNOWN_TRIGGERS_NUM, u32);
	SET_FTM(reschedule_requests_num, RESCHEDULE_REQUESTS_NUM, u32);
	SET_FTM(out_of_window_triggers_num, OUT_OF_WINDOW_TRIGGERS_NUM, u32);
#undef SET_FTM

	nla_nest_end(msg, ftm_stats_attr);

	genlmsg_end(msg, hdr);
	return genlmsg_reply(msg, info);

nla_put_failure:
	nlmsg_free(msg);
	return -ENOBUFS;
}


i8254: fix out-of-bounds memory access in pit_ioport_read()

Due converting PIO to the new memory read/write api we no longer provide
separate I/O region lenghts for read and write operations. As a result,
reading from PIT Mode/Command register will end with accessing
pit->channels with invalid index.

Fix this by ignoring read from the Mode/Command register.

This is CVE-2015-3214.

Reported-by: Matt Tait <matttait@google.com>
Fixes: 0505bcdec8228d8de39ab1a02644e71999e7c052
Cc: qemu-stable@nongnu.org
Signed-off-by: Petr Matousek <pmatouse@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
                                unsigned size)
{
    PITCommonState *pit = opaque;
    int ret, count;
    PITChannelState *s;

    addr &= 3;
    s = &pit->channels[addr];
    if (s->status_latched) {
        s->status_latched = 0;
        ret = s->status;
    } else if (s->count_latched) {
        switch(s->count_latched) {
        default:
        case RW_STATE_LSB:
            ret = s->latched_count & 0xff;
            s->count_latched = 0;
            break;
        case RW_STATE_MSB:
            ret = s->latched_count >> 8;
            s->count_latched = 0;
            break;
        case RW_STATE_WORD0:
            ret = s->latched_count & 0xff;
            s->count_latched = RW_STATE_MSB;
            break;
        }
    } else {
        switch(s->read_state) {
        default:
        case RW_STATE_LSB:
            count = pit_get_count(s);
            ret = count & 0xff;
            break;
        case RW_STATE_MSB:
            count = pit_get_count(s);
            ret = (count >> 8) & 0xff;
            break;
        case RW_STATE_WORD0:
            count = pit_get_count(s);
            ret = count & 0xff;
            s->read_state = RW_STATE_WORD1;
            break;
        case RW_STATE_WORD1:
            count = pit_get_count(s);
            ret = (count >> 8) & 0xff;
            s->read_state = RW_STATE_WORD0;
            break;
        }
    }
    return ret;
}

The pit_ioport_read in i8254.c in the Linux kernel before 2.6.33 and QEMU before 2.3.1 does not distinguish between read lengths and write lengths, which might allow guest OS users to execute arbitrary code on the host OS by triggering use of an invalid index.


static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
                                unsigned size)
{
    PITCommonState *pit = opaque;
    int ret, count;
    PITChannelState *s;

    addr &= 3;

    if (addr == 3) {
        /* Mode/Command register is write only, read is ignored */
        return 0;
    }

    s = &pit->channels[addr];
    if (s->status_latched) {
        s->status_latched = 0;
        ret = s->status;
    } else if (s->count_latched) {
        switch(s->count_latched) {
        default:
        case RW_STATE_LSB:
            ret = s->latched_count & 0xff;
            s->count_latched = 0;
            break;
        case RW_STATE_MSB:
            ret = s->latched_count >> 8;
            s->count_latched = 0;
            break;
        case RW_STATE_WORD0:
            ret = s->latched_count & 0xff;
            s->count_latched = RW_STATE_MSB;
            break;
        }
    } else {
        switch(s->read_state) {
        default:
        case RW_STATE_LSB:
            count = pit_get_count(s);
            ret = count & 0xff;
            break;
        case RW_STATE_MSB:
            count = pit_get_count(s);
            ret = (count >> 8) & 0xff;
            break;
        case RW_STATE_WORD0:
            count = pit_get_count(s);
            ret = count & 0xff;
            s->read_state = RW_STATE_WORD1;
            break;
        case RW_STATE_WORD1:
            count = pit_get_count(s);
            ret = (count >> 8) & 0xff;
            s->read_state = RW_STATE_WORD0;
            break;
        }
    }
    return ret;
}


patch 8.2.3428: using freed memory when replacing

Problem:    Using freed memory when replacing. (Dhiraj Mishra)
Solution:   Get the line pointer after calling ins_copychar().

nv_replace(cmdarg_T *cap)
{
    char_u	*ptr;
    int		had_ctrl_v;
    long	n;

    if (checkclearop(cap->oap))
	return;
#ifdef FEAT_JOB_CHANNEL
    if (bt_prompt(curbuf) && !prompt_curpos_editable())
    {
	clearopbeep(cap->oap);
	return;
    }
#endif

    // get another character
    if (cap->nchar == Ctrl_V)
    {
	had_ctrl_v = Ctrl_V;
	cap->nchar = get_literal(FALSE);
	// Don't redo a multibyte character with CTRL-V.
	if (cap->nchar > DEL)
	    had_ctrl_v = NUL;
    }
    else
	had_ctrl_v = NUL;

    // Abort if the character is a special key.
    if (IS_SPECIAL(cap->nchar))
    {
	clearopbeep(cap->oap);
	return;
    }

    // Visual mode "r"
    if (VIsual_active)
    {
	if (got_int)
	    reset_VIsual();
	if (had_ctrl_v)
	{
	    // Use a special (negative) number to make a difference between a
	    // literal CR or NL and a line break.
	    if (cap->nchar == CAR)
		cap->nchar = REPLACE_CR_NCHAR;
	    else if (cap->nchar == NL)
		cap->nchar = REPLACE_NL_NCHAR;
	}
	nv_operator(cap);
	return;
    }

    // Break tabs, etc.
    if (virtual_active())
    {
	if (u_save_cursor() == FAIL)
	    return;
	if (gchar_cursor() == NUL)
	{
	    // Add extra space and put the cursor on the first one.
	    coladvance_force((colnr_T)(getviscol() + cap->count1));
	    curwin->w_cursor.col -= cap->count1;
	}
	else if (gchar_cursor() == TAB)
	    coladvance_force(getviscol());
    }

    // Abort if not enough characters to replace.
    ptr = ml_get_cursor();
    if (STRLEN(ptr) < (unsigned)cap->count1
	    || (has_mbyte && mb_charlen(ptr) < cap->count1))
    {
	clearopbeep(cap->oap);
	return;
    }

    /*
     * Replacing with a TAB is done by edit() when it is complicated because
     * 'expandtab' or 'smarttab' is set.  CTRL-V TAB inserts a literal TAB.
     * Other characters are done below to avoid problems with things like
     * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
     */
    if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
    {
	stuffnumReadbuff(cap->count1);
	stuffcharReadbuff('R');
	stuffcharReadbuff('\t');
	stuffcharReadbuff(ESC);
	return;
    }

    // save line for undo
    if (u_save_cursor() == FAIL)
	return;

    if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n'))
    {
	/*
	 * Replace character(s) by a single newline.
	 * Strange vi behaviour: Only one newline is inserted.
	 * Delete the characters here.
	 * Insert the newline with an insert command, takes care of
	 * autoindent.	The insert command depends on being on the last
	 * character of a line or not.
	 */
	(void)del_chars(cap->count1, FALSE);	// delete the characters
	stuffcharReadbuff('\r');
	stuffcharReadbuff(ESC);

	// Give 'r' to edit(), to get the redo command right.
	invoke_edit(cap, TRUE, 'r', FALSE);
    }
    else
    {
	prep_redo(cap->oap->regname, cap->count1,
				       NUL, 'r', NUL, had_ctrl_v, cap->nchar);

	curbuf->b_op_start = curwin->w_cursor;
	if (has_mbyte)
	{
	    int		old_State = State;

	    if (cap->ncharC1 != 0)
		AppendCharToRedobuff(cap->ncharC1);
	    if (cap->ncharC2 != 0)
		AppendCharToRedobuff(cap->ncharC2);

	    // This is slow, but it handles replacing a single-byte with a
	    // multi-byte and the other way around.  Also handles adding
	    // composing characters for utf-8.
	    for (n = cap->count1; n > 0; --n)
	    {
		State = REPLACE;
		if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
		{
		    int c = ins_copychar(curwin->w_cursor.lnum
					   + (cap->nchar == Ctrl_Y ? -1 : 1));
		    if (c != NUL)
			ins_char(c);
		    else
			// will be decremented further down
			++curwin->w_cursor.col;
		}
		else
		    ins_char(cap->nchar);
		State = old_State;
		if (cap->ncharC1 != 0)
		    ins_char(cap->ncharC1);
		if (cap->ncharC2 != 0)
		    ins_char(cap->ncharC2);
	    }
	}
	else
	{
	    /*
	     * Replace the characters within one line.
	     */
	    for (n = cap->count1; n > 0; --n)
	    {
		/*
		 * Get ptr again, because u_save and/or showmatch() will have
		 * released the line.  At the same time we let know that the
		 * line will be changed.
		 */
		ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
		if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
		{
		  int c = ins_copychar(curwin->w_cursor.lnum
					   + (cap->nchar == Ctrl_Y ? -1 : 1));
		  if (c != NUL)
		    ptr[curwin->w_cursor.col] = c;
		}
		else
		    ptr[curwin->w_cursor.col] = cap->nchar;
		if (p_sm && msg_silent == 0)
		    showmatch(cap->nchar);
		++curwin->w_cursor.col;
	    }
#ifdef FEAT_NETBEANS_INTG
	    if (netbeans_active())
	    {
		colnr_T  start = (colnr_T)(curwin->w_cursor.col - cap->count1);

		netbeans_removed(curbuf, curwin->w_cursor.lnum, start,
							   (long)cap->count1);
		netbeans_inserted(curbuf, curwin->w_cursor.lnum, start,
					       &ptr[start], (int)cap->count1);
	    }
#endif

	    // mark the buffer as changed and prepare for displaying
	    changed_bytes(curwin->w_cursor.lnum,
			       (colnr_T)(curwin->w_cursor.col - cap->count1));
	}
	--curwin->w_cursor.col;	    // cursor on the last replaced char
	// if the character on the left of the current cursor is a multi-byte
	// character, move two characters left
	if (has_mbyte)
	    mb_adjust_cursor();
	curbuf->b_op_end = curwin->w_cursor;
	curwin->w_set_curswant = TRUE;
	set_last_insert(cap->nchar);
    }
}


nv_replace(cmdarg_T *cap)
{
    char_u	*ptr;
    int		had_ctrl_v;
    long	n;

    if (checkclearop(cap->oap))
	return;
#ifdef FEAT_JOB_CHANNEL
    if (bt_prompt(curbuf) && !prompt_curpos_editable())
    {
	clearopbeep(cap->oap);
	return;
    }
#endif

    // get another character
    if (cap->nchar == Ctrl_V)
    {
	had_ctrl_v = Ctrl_V;
	cap->nchar = get_literal(FALSE);
	// Don't redo a multibyte character with CTRL-V.
	if (cap->nchar > DEL)
	    had_ctrl_v = NUL;
    }
    else
	had_ctrl_v = NUL;

    // Abort if the character is a special key.
    if (IS_SPECIAL(cap->nchar))
    {
	clearopbeep(cap->oap);
	return;
    }

    // Visual mode "r"
    if (VIsual_active)
    {
	if (got_int)
	    reset_VIsual();
	if (had_ctrl_v)
	{
	    // Use a special (negative) number to make a difference between a
	    // literal CR or NL and a line break.
	    if (cap->nchar == CAR)
		cap->nchar = REPLACE_CR_NCHAR;
	    else if (cap->nchar == NL)
		cap->nchar = REPLACE_NL_NCHAR;
	}
	nv_operator(cap);
	return;
    }

    // Break tabs, etc.
    if (virtual_active())
    {
	if (u_save_cursor() == FAIL)
	    return;
	if (gchar_cursor() == NUL)
	{
	    // Add extra space and put the cursor on the first one.
	    coladvance_force((colnr_T)(getviscol() + cap->count1));
	    curwin->w_cursor.col -= cap->count1;
	}
	else if (gchar_cursor() == TAB)
	    coladvance_force(getviscol());
    }

    // Abort if not enough characters to replace.
    ptr = ml_get_cursor();
    if (STRLEN(ptr) < (unsigned)cap->count1
	    || (has_mbyte && mb_charlen(ptr) < cap->count1))
    {
	clearopbeep(cap->oap);
	return;
    }

    /*
     * Replacing with a TAB is done by edit() when it is complicated because
     * 'expandtab' or 'smarttab' is set.  CTRL-V TAB inserts a literal TAB.
     * Other characters are done below to avoid problems with things like
     * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
     */
    if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta))
    {
	stuffnumReadbuff(cap->count1);
	stuffcharReadbuff('R');
	stuffcharReadbuff('\t');
	stuffcharReadbuff(ESC);
	return;
    }

    // save line for undo
    if (u_save_cursor() == FAIL)
	return;

    if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n'))
    {
	/*
	 * Replace character(s) by a single newline.
	 * Strange vi behaviour: Only one newline is inserted.
	 * Delete the characters here.
	 * Insert the newline with an insert command, takes care of
	 * autoindent.	The insert command depends on being on the last
	 * character of a line or not.
	 */
	(void)del_chars(cap->count1, FALSE);	// delete the characters
	stuffcharReadbuff('\r');
	stuffcharReadbuff(ESC);

	// Give 'r' to edit(), to get the redo command right.
	invoke_edit(cap, TRUE, 'r', FALSE);
    }
    else
    {
	prep_redo(cap->oap->regname, cap->count1,
				       NUL, 'r', NUL, had_ctrl_v, cap->nchar);

	curbuf->b_op_start = curwin->w_cursor;
	if (has_mbyte)
	{
	    int		old_State = State;

	    if (cap->ncharC1 != 0)
		AppendCharToRedobuff(cap->ncharC1);
	    if (cap->ncharC2 != 0)
		AppendCharToRedobuff(cap->ncharC2);

	    // This is slow, but it handles replacing a single-byte with a
	    // multi-byte and the other way around.  Also handles adding
	    // composing characters for utf-8.
	    for (n = cap->count1; n > 0; --n)
	    {
		State = REPLACE;
		if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
		{
		    int c = ins_copychar(curwin->w_cursor.lnum
					   + (cap->nchar == Ctrl_Y ? -1 : 1));
		    if (c != NUL)
			ins_char(c);
		    else
			// will be decremented further down
			++curwin->w_cursor.col;
		}
		else
		    ins_char(cap->nchar);
		State = old_State;
		if (cap->ncharC1 != 0)
		    ins_char(cap->ncharC1);
		if (cap->ncharC2 != 0)
		    ins_char(cap->ncharC2);
	    }
	}
	else
	{
	    /*
	     * Replace the characters within one line.
	     */
	    for (n = cap->count1; n > 0; --n)
	    {
		/*
		 * Get ptr again, because u_save and/or showmatch() will have
		 * released the line.  This may also happen in ins_copychar().
		 * At the same time we let know that the line will be changed.
		 */
		if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y)
		{
		  int c = ins_copychar(curwin->w_cursor.lnum
					   + (cap->nchar == Ctrl_Y ? -1 : 1));

		  ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
		  if (c != NUL)
		    ptr[curwin->w_cursor.col] = c;
		}
		else
		{
		    ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
		    ptr[curwin->w_cursor.col] = cap->nchar;
		}
		if (p_sm && msg_silent == 0)
		    showmatch(cap->nchar);
		++curwin->w_cursor.col;
	    }
#ifdef FEAT_NETBEANS_INTG
	    if (netbeans_active())
	    {
		colnr_T  start = (colnr_T)(curwin->w_cursor.col - cap->count1);

		netbeans_removed(curbuf, curwin->w_cursor.lnum, start,
							   (long)cap->count1);
		netbeans_inserted(curbuf, curwin->w_cursor.lnum, start,
					       &ptr[start], (int)cap->count1);
	    }
#endif

	    // mark the buffer as changed and prepare for displaying
	    changed_bytes(curwin->w_cursor.lnum,
			       (colnr_T)(curwin->w_cursor.col - cap->count1));
	}
	--curwin->w_cursor.col;	    // cursor on the last replaced char
	// if the character on the left of the current cursor is a multi-byte
	// character, move two characters left
	if (has_mbyte)
	    mb_adjust_cursor();
	curbuf->b_op_end = curwin->w_cursor;
	curwin->w_set_curswant = TRUE;
	set_last_insert(cap->nchar);
    }
}


Fixed memory leak reported in #1558 and fixed other leak.

static Image *ReadDOTImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  char
    command[MagickPathExtent];

  const char
    *option;

  graph_t
    *graph;

  Image
    *image;

  ImageInfo
    *read_info;

  MagickBooleanType
    status;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  assert(graphic_context != (GVC_t *) NULL);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  read_info=CloneImageInfo(image_info);
  SetImageInfoBlob(read_info,(void *) NULL,0);
  (void) CopyMagickString(read_info->magick,"SVG",MagickPathExtent);
  (void) AcquireUniqueFilename(read_info->filename);
  (void) FormatLocaleString(command,MagickPathExtent,"-Tsvg -o%s %s",
    read_info->filename,image_info->filename);
#if !defined(WITH_CGRAPH)
  graph=agread(GetBlobFileHandle(image));
#else
  graph=agread(GetBlobFileHandle(image),(Agdisc_t *) NULL);
#endif
  if (graph == (graph_t *) NULL)
    {
      (void) RelinquishUniqueFileResource(read_info->filename);
      return ((Image *) NULL);
    }
  option=GetImageOption(image_info,"dot:layout-engine");
  if (option == (const char *) NULL)
    gvLayout(graphic_context,graph,(char *) "dot");
  else
    gvLayout(graphic_context,graph,(char *) option);
  gvRenderFilename(graphic_context,graph,(char *) "svg",read_info->filename);
  gvFreeLayout(graphic_context,graph);
  agclose(graph);
  /*
    Read SVG graph.
  */
  (void) CopyMagickString(read_info->magick,"SVG",MaxTextExtent);
  image=ReadImage(read_info,exception);
  (void) RelinquishUniqueFileResource(read_info->filename);
  read_info=DestroyImageInfo(read_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  return(GetFirstImageInList(image));
}

ImageMagick 7.0.8-43 has a memory leak in coders/dot.c, as demonstrated by PingImage in MagickCore/constitute.c.


static Image *ReadDOTImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  char
    command[MagickPathExtent];

  const char
    *option;

  graph_t
    *graph;

  Image
    *image;

  ImageInfo
    *read_info;

  MagickBooleanType
    status;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  assert(graphic_context != (GVC_t *) NULL);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));
  read_info=CloneImageInfo(image_info);
  SetImageInfoBlob(read_info,(void *) NULL,0);
  (void) CopyMagickString(read_info->magick,"SVG",MagickPathExtent);
  (void) AcquireUniqueFilename(read_info->filename);
  (void) FormatLocaleString(command,MagickPathExtent,"-Tsvg -o%s %s",
    read_info->filename,image_info->filename);
#if !defined(WITH_CGRAPH)
  graph=agread(GetBlobFileHandle(image));
#else
  graph=agread(GetBlobFileHandle(image),(Agdisc_t *) NULL);
#endif
  if (graph == (graph_t *) NULL)
    {
      (void) RelinquishUniqueFileResource(read_info->filename);
      return(DestroyImageList(image));
    }
  option=GetImageOption(image_info,"dot:layout-engine");
  if (option == (const char *) NULL)
    gvLayout(graphic_context,graph,(char *) "dot");
  else
    gvLayout(graphic_context,graph,(char *) option);
  gvRenderFilename(graphic_context,graph,(char *) "svg",read_info->filename);
  gvFreeLayout(graphic_context,graph);
  agclose(graph);
  image=DestroyImageList(image);
  /*
    Read SVG graph.
  */
  (void) CopyMagickString(read_info->magick,"SVG",MaxTextExtent);
  image=ReadImage(read_info,exception);
  (void) RelinquishUniqueFileResource(read_info->filename);
  read_info=DestroyImageInfo(read_info);
  if (image == (Image *) NULL)
    return((Image *) NULL);
  return(GetFirstImageInList(image));
}