ast_for_arguments(struct compiling *c, const node *n)
{
    /* This function handles both typedargslist (function definition)
       and varargslist (lambda definition).

       parameters: '(' [typedargslist] ')'
       typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
               '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
             | '**' tfpdef [',']]]
         | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
         | '**' tfpdef [','])
       tfpdef: NAME [':' test]
       varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
               '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
             | '**' vfpdef [',']]]
         | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
         | '**' vfpdef [',']
       )
       vfpdef: NAME

    */
    int i, j, k, nposargs = 0, nkwonlyargs = 0;
    int nposdefaults = 0, found_default = 0;
    asdl_seq *posargs, *posdefaults, *kwonlyargs, *kwdefaults;
    arg_ty vararg = NULL, kwarg = NULL;
    arg_ty arg = NULL;
    node *ch;

    if (TYPE(n) == parameters) {
        if (NCH(n) == 2) /* () as argument list */
            return arguments(NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena);
        n = CHILD(n, 1);
    }
    assert(TYPE(n) == typedargslist || TYPE(n) == varargslist);

    /* First count the number of positional args & defaults.  The
       variable i is the loop index for this for loop and the next.
       The next loop picks up where the first leaves off.
    */
    for (i = 0; i < NCH(n); i++) {
        ch = CHILD(n, i);
        if (TYPE(ch) == STAR) {
            /* skip star */
            i++;
            if (i < NCH(n) && /* skip argument following star */
                (TYPE(CHILD(n, i)) == tfpdef ||
                 TYPE(CHILD(n, i)) == vfpdef)) {
                i++;
            }
            break;
        }
        if (TYPE(ch) == DOUBLESTAR) break;
        if (TYPE(ch) == vfpdef || TYPE(ch) == tfpdef) nposargs++;
        if (TYPE(ch) == EQUAL) nposdefaults++;
    }
    /* count the number of keyword only args &
       defaults for keyword only args */
    for ( ; i < NCH(n); ++i) {
        ch = CHILD(n, i);
        if (TYPE(ch) == DOUBLESTAR) break;
        if (TYPE(ch) == tfpdef || TYPE(ch) == vfpdef) nkwonlyargs++;
    }
    posargs = (nposargs ? _Py_asdl_seq_new(nposargs, c->c_arena) : NULL);
    if (!posargs && nposargs)
        return NULL;
    kwonlyargs = (nkwonlyargs ?
                   _Py_asdl_seq_new(nkwonlyargs, c->c_arena) : NULL);
    if (!kwonlyargs && nkwonlyargs)
        return NULL;
    posdefaults = (nposdefaults ?
                    _Py_asdl_seq_new(nposdefaults, c->c_arena) : NULL);
    if (!posdefaults && nposdefaults)
        return NULL;
    /* The length of kwonlyargs and kwdefaults are same
       since we set NULL as default for keyword only argument w/o default
       - we have sequence data structure, but no dictionary */
    kwdefaults = (nkwonlyargs ?
                   _Py_asdl_seq_new(nkwonlyargs, c->c_arena) : NULL);
    if (!kwdefaults && nkwonlyargs)
        return NULL;

    /* tfpdef: NAME [':' test]
       vfpdef: NAME
    */
    i = 0;
    j = 0;  /* index for defaults */
    k = 0;  /* index for args */
    while (i < NCH(n)) {
        ch = CHILD(n, i);
        switch (TYPE(ch)) {
            case tfpdef:
            case vfpdef:
                /* XXX Need to worry about checking if TYPE(CHILD(n, i+1)) is
                   anything other than EQUAL or a comma? */
                /* XXX Should NCH(n) check be made a separate check? */
                if (i + 1 < NCH(n) && TYPE(CHILD(n, i + 1)) == EQUAL) {
                    expr_ty expression = ast_for_expr(c, CHILD(n, i + 2));
                    if (!expression)
                        return NULL;
                    assert(posdefaults != NULL);
                    asdl_seq_SET(posdefaults, j++, expression);
                    i += 2;
                    found_default = 1;
                }
                else if (found_default) {
                    ast_error(c, n,
                              "non-default argument follows default argument");
                    return NULL;
                }
                arg = ast_for_arg(c, ch);
                if (!arg)
                    return NULL;
                asdl_seq_SET(posargs, k++, arg);
                i += 1; /* the name */
                if (i < NCH(n) && TYPE(CHILD(n, i)) == COMMA)
                    i += 1; /* the comma, if present */
                break;
            case STAR:
                if (i+1 >= NCH(n) ||
                    (i+2 == NCH(n) && (TYPE(CHILD(n, i+1)) == COMMA
                                       || TYPE(CHILD(n, i+1)) == TYPE_COMMENT))) {
                    ast_error(c, CHILD(n, i),
                              "named arguments must follow bare *");
                    return NULL;
                }
                ch = CHILD(n, i+1);  /* tfpdef or COMMA */
                if (TYPE(ch) == COMMA) {
                    int res = 0;
                    i += 2; /* now follows keyword only arguments */

                    if (i < NCH(n) && TYPE(CHILD(n, i)) == TYPE_COMMENT) {
                        ast_error(c, CHILD(n, i),
                                  "bare * has associated type comment");
                        return NULL;
                    }

                    res = handle_keywordonly_args(c, n, i,
                                                  kwonlyargs, kwdefaults);
                    if (res == -1) return NULL;
                    i = res; /* res has new position to process */
                }
                else {
                    vararg = ast_for_arg(c, ch);
                    if (!vararg)
                        return NULL;

                i += 2; /* the star and the name */
                if (i < NCH(n) && TYPE(CHILD(n, i)) == COMMA)
                    i += 1; /* the comma, if present */

                if (i < NCH(n) && TYPE(CHILD(n, i)) == TYPE_COMMENT) {
                        vararg->type_comment = NEW_TYPE_COMMENT(CHILD(n, i));
                        if (!vararg->type_comment)
                            return NULL;
                        i += 1;
                    }

                    if (i < NCH(n) && (TYPE(CHILD(n, i)) == tfpdef
                                    || TYPE(CHILD(n, i)) == vfpdef)) {
                        int res = 0;
                        res = handle_keywordonly_args(c, n, i,
                                                      kwonlyargs, kwdefaults);
                        if (res == -1) return NULL;
                        i = res; /* res has new position to process */
                    }
                }
                break;
            case DOUBLESTAR:
                ch = CHILD(n, i+1);  /* tfpdef */
                assert(TYPE(ch) == tfpdef || TYPE(ch) == vfpdef);
                kwarg = ast_for_arg(c, ch);
                if (!kwarg)
                    return NULL;
                i += 2; /* the double star and the name */
                if (i < NCH(n) && TYPE(CHILD(n, i)) == COMMA)
                    i += 1; /* the comma, if present */
                break;
            case TYPE_COMMENT:
                assert(i);

                if (kwarg)
                    arg = kwarg;

                /* arg will be equal to the last argument processed */
                arg->type_comment = NEW_TYPE_COMMENT(ch);
                if (!arg->type_comment)
                    return NULL;
                i += 1;
                break;
            default:
                PyErr_Format(PyExc_SystemError,
                             "unexpected node in varargslist: %d @ %d",
                             TYPE(ch), i);
                return NULL;
        }
    }
    return arguments(posargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults, c->c_arena);
}


bpo-36495: Fix two out-of-bounds array reads (GH-12641)

Research and fix by @bradlarsen.

handle_keywordonly_args(struct compiling *c, const node *n, int start,
                        asdl_seq *kwonlyargs, asdl_seq *kwdefaults)
{
    PyObject *argname;
    node *ch;
    expr_ty expression, annotation;
    arg_ty arg = NULL;
    int i = start;
    int j = 0; /* index for kwdefaults and kwonlyargs */

    if (kwonlyargs == NULL) {
        ast_error(c, CHILD(n, start), "named arguments must follow bare *");
        return -1;
    }
    assert(kwdefaults != NULL);
    while (i < NCH(n)) {
        ch = CHILD(n, i);
        switch (TYPE(ch)) {
            case vfpdef:
            case tfpdef:
                if (i + 1 < NCH(n) && TYPE(CHILD(n, i + 1)) == EQUAL) {
                    expression = ast_for_expr(c, CHILD(n, i + 2));
                    if (!expression)
                        goto error;
                    asdl_seq_SET(kwdefaults, j, expression);
                    i += 2; /* '=' and test */
                }
                else { /* setting NULL if no default value exists */
                    asdl_seq_SET(kwdefaults, j, NULL);
                }
                if (NCH(ch) == 3) {
                    /* ch is NAME ':' test */
                    annotation = ast_for_expr(c, CHILD(ch, 2));
                    if (!annotation)
                        goto error;
                }
                else {
                    annotation = NULL;
                }
                ch = CHILD(ch, 0);
                argname = NEW_IDENTIFIER(ch);
                if (!argname)
                    goto error;
                if (forbidden_name(c, argname, ch, 0))
                    goto error;
                arg = arg(argname, annotation, NULL, LINENO(ch), ch->n_col_offset,
                          ch->n_end_lineno, ch->n_end_col_offset,
                          c->c_arena);
                if (!arg)
                    goto error;
                asdl_seq_SET(kwonlyargs, j++, arg);
                i += 1; /* the name */
                if (TYPE(CHILD(n, i)) == COMMA)
                    i += 1; /* the comma, if present */
                break;
            case TYPE_COMMENT:
                /* arg will be equal to the last argument processed */
                arg->type_comment = NEW_TYPE_COMMENT(ch);
                if (!arg->type_comment)
                    goto error;
                i += 1;
                break;
            case DOUBLESTAR:
                return i;
            default:
                ast_error(c, ch, "unexpected node");
                goto error;
        }
    }
    return i;
 error:
    return -1;
}

typed_ast 1.3.0 and 1.3.1 has an ast_for_arguments out-of-bounds read. An attacker with the ability to cause a Python interpreter to parse Python source (but not necessarily execute it) may be able to crash the interpreter process. This could be a concern, for example, in a web-based service that parses (but does not execute) Python code. (This issue also affected certain Python 3.8.0-alpha prereleases.)


handle_keywordonly_args(struct compiling *c, const node *n, int start,
                        asdl_seq *kwonlyargs, asdl_seq *kwdefaults)
{
    PyObject *argname;
    node *ch;
    expr_ty expression, annotation;
    arg_ty arg = NULL;
    int i = start;
    int j = 0; /* index for kwdefaults and kwonlyargs */

    if (kwonlyargs == NULL) {
        ast_error(c, CHILD(n, start), "named arguments must follow bare *");
        return -1;
    }
    assert(kwdefaults != NULL);
    while (i < NCH(n)) {
        ch = CHILD(n, i);
        switch (TYPE(ch)) {
            case vfpdef:
            case tfpdef:
                if (i + 1 < NCH(n) && TYPE(CHILD(n, i + 1)) == EQUAL) {
                    expression = ast_for_expr(c, CHILD(n, i + 2));
                    if (!expression)
                        goto error;
                    asdl_seq_SET(kwdefaults, j, expression);
                    i += 2; /* '=' and test */
                }
                else { /* setting NULL if no default value exists */
                    asdl_seq_SET(kwdefaults, j, NULL);
                }
                if (NCH(ch) == 3) {
                    /* ch is NAME ':' test */
                    annotation = ast_for_expr(c, CHILD(ch, 2));
                    if (!annotation)
                        goto error;
                }
                else {
                    annotation = NULL;
                }
                ch = CHILD(ch, 0);
                argname = NEW_IDENTIFIER(ch);
                if (!argname)
                    goto error;
                if (forbidden_name(c, argname, ch, 0))
                    goto error;
                arg = arg(argname, annotation, NULL, LINENO(ch), ch->n_col_offset,
                          ch->n_end_lineno, ch->n_end_col_offset,
                          c->c_arena);
                if (!arg)
                    goto error;
                asdl_seq_SET(kwonlyargs, j++, arg);
                i += 1; /* the name */
                if (i < NCH(n) && TYPE(CHILD(n, i)) == COMMA)
                    i += 1; /* the comma, if present */
                break;
            case TYPE_COMMENT:
                /* arg will be equal to the last argument processed */
                arg->type_comment = NEW_TYPE_COMMENT(ch);
                if (!arg->type_comment)
                    goto error;
                i += 1;
                break;
            case DOUBLESTAR:
                return i;
            default:
                ast_error(c, ch, "unexpected node");
                goto error;
        }
    }
    return i;
 error:
    return -1;
}


fix recursion level crash (bb #3706).

Thanks to Stephane Chazelas for the analysis.

int32_t cli_bcapi_extract_new(struct cli_bc_ctx *ctx, int32_t id)
{
    cli_ctx *cctx;
    int res = -1;

    cli_event_count(EV, BCEV_EXTRACTED);
    cli_dbgmsg("previous tempfile had %u bytes\n", ctx->written);
    if (!ctx->written)
	return 0;
    if (ctx->ctx && cli_updatelimits(ctx->ctx, ctx->written))
	return -1;
    ctx->written = 0;
    lseek(ctx->outfd, 0, SEEK_SET);
    cli_dbgmsg("bytecode: scanning extracted file %s\n", ctx->tempfile);
    cctx = (cli_ctx*)ctx->ctx;
    if (cctx) {
	cli_file_t current = cctx->container_type;
	if (ctx->containertype != CL_TYPE_ANY)
	    cctx->container_type = ctx->containertype;
	res = cli_magic_scandesc(ctx->outfd, cctx);
	cctx->container_type = current;
	if (res == CL_VIRUS) {
	    if (cctx->virname)
		ctx->virname = *cctx->virname;
	    ctx->found = 1;
	}
    }
    if ((cctx && cctx->engine->keeptmp) ||
	(ftruncate(ctx->outfd, 0) == -1)) {

	close(ctx->outfd);
	if (!(cctx && cctx->engine->keeptmp) && ctx->tempfile)
	    cli_unlink(ctx->tempfile);
	free(ctx->tempfile);
	ctx->tempfile = NULL;
	ctx->outfd = 0;
    }
    cli_dbgmsg("bytecode: extracting new file with id %u\n", id);
    return res;
}

The bytecode engine in ClamAV before 0.97.3 allows remote attackers to cause a denial of service (crash) via vectors related to "recursion level" and (1) libclamav/bytecode.c and (2) libclamav/bytecode_api.c.


int32_t cli_bcapi_extract_new(struct cli_bc_ctx *ctx, int32_t id)
{
    cli_ctx *cctx;
    int res = -1;

    cli_event_count(EV, BCEV_EXTRACTED);
    cli_dbgmsg("previous tempfile had %u bytes\n", ctx->written);
    if (!ctx->written)
	return 0;
    if (ctx->ctx && cli_updatelimits(ctx->ctx, ctx->written))
	return -1;
    ctx->written = 0;
    lseek(ctx->outfd, 0, SEEK_SET);
    cli_dbgmsg("bytecode: scanning extracted file %s\n", ctx->tempfile);
    cctx = (cli_ctx*)ctx->ctx;
    if (cctx) {
	cli_file_t current = cctx->container_type;
	if (ctx->containertype != CL_TYPE_ANY)
	    cctx->container_type = ctx->containertype;
	cctx->recursion++;
	res = cli_magic_scandesc(ctx->outfd, cctx);
	cctx->recursion--;
	cctx->container_type = current;
	if (res == CL_VIRUS) {
	    if (cctx->virname)
		ctx->virname = *cctx->virname;
	    ctx->found = 1;
	}
    }
    if ((cctx && cctx->engine->keeptmp) ||
	(ftruncate(ctx->outfd, 0) == -1)) {

	close(ctx->outfd);
	if (!(cctx && cctx->engine->keeptmp) && ctx->tempfile)
	    cli_unlink(ctx->tempfile);
	free(ctx->tempfile);
	ctx->tempfile = NULL;
	ctx->outfd = 0;
    }
    cli_dbgmsg("bytecode: extracting new file with id %u\n", id);
    return res;
}


int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
			 unsigned id, fmap_t *map, const char **virname)
{
    const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
    unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
    int ret;
    unsigned executed = 0, breakflag = 0, errorflag = 0;

    cli_dbgmsg("Bytecode executing hook id %u (%u hooks)\n", id, hooks_cnt);
    /* restore match counts */
    cli_bytecode_context_setfile(ctx, map);
    ctx->hooks.match_counts = ctx->lsigcnt;
    ctx->hooks.match_offsets = ctx->lsigoff;
    for (i=0;i < hooks_cnt;i++) {
	const struct cli_bc *bc = &engine->bcs.all_bcs[hooks[i]];
	if (bc->lsig) {
	    if (!cctx->hook_lsig_matches ||
		!cli_bitset_test(cctx->hook_lsig_matches, bc->hook_lsig_id-1))
		continue;
	    cli_dbgmsg("Bytecode: executing bytecode %u (lsig matched)\n" , bc->id);
	}
	cli_bytecode_context_setfuncid(ctx, bc, 0);
	ret = cli_bytecode_run(&engine->bcs, bc, ctx);
	executed++;
	if (ret != CL_SUCCESS) {
	    cli_warnmsg("Bytecode %u failed to run: %s\n", bc->id, cl_strerror(ret));
	    errorflag = 1;
	    continue;
	}
	if (ctx->virname) {
	    cli_dbgmsg("Bytecode found virus: %s\n", ctx->virname);
	    if (virname)
		*virname = ctx->virname;
	    cli_bytecode_context_clear(ctx);
	    return CL_VIRUS;
	}
	ret = cli_bytecode_context_getresult_int(ctx);
	/* TODO: use prefix here */
	cli_dbgmsg("Bytecode %u returned %u\n", bc->id, ret);
	if (ret == 0xcea5e) {
	    cli_dbgmsg("Bytecode set BREAK flag in hook!\n");
	    breakflag = 1;
	}
	if (!ret) {
	    char *tempfile;
	    int fd = cli_bytecode_context_getresult_file(ctx, &tempfile);
	    if (fd && fd != -1) {
		if (cctx && cctx->engine->keeptmp)
		    cli_dbgmsg("Bytecode %u unpacked file saved in %s\n",
			       bc->id, tempfile);
		else
		    cli_dbgmsg("Bytecode %u unpacked file\n", bc->id);
		lseek(fd, 0, SEEK_SET);
		cli_dbgmsg("***** Scanning unpacked file ******\n");
		ret = cli_magic_scandesc(fd, cctx);
		if (!cctx || !cctx->engine->keeptmp)
		    if (ftruncate(fd, 0) == -1)
			cli_dbgmsg("ftruncate failed on %d\n", fd);
		close(fd);
		if (!cctx || !cctx->engine->keeptmp) {
		    if (tempfile && cli_unlink(tempfile))
			ret = CL_EUNLINK;
		}
		free(tempfile);
		if (ret != CL_CLEAN) {
		    if (ret == CL_VIRUS)
			cli_dbgmsg("Scanning unpacked file by bytecode %u found a virus\n", bc->id);
		    cli_bytecode_context_clear(ctx);
		    return ret;
		}
		cli_bytecode_context_reset(ctx);
		continue;
	    }
	}
	cli_bytecode_context_reset(ctx);
    }
    if (executed)
	cli_dbgmsg("Bytecode: executed %u bytecodes for this hook\n", executed);
    else
	cli_dbgmsg("Bytecode: no logical signature matched, no bytecode executed\n");
    if (errorflag && cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
	return CL_EBYTECODE_TESTFAIL;
    return breakflag ? CL_BREAK : CL_CLEAN;
}


int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
			 unsigned id, fmap_t *map, const char **virname)
{
    const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
    unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
    int ret;
    unsigned executed = 0, breakflag = 0, errorflag = 0;

    cli_dbgmsg("Bytecode executing hook id %u (%u hooks)\n", id, hooks_cnt);
    /* restore match counts */
    cli_bytecode_context_setfile(ctx, map);
    ctx->hooks.match_counts = ctx->lsigcnt;
    ctx->hooks.match_offsets = ctx->lsigoff;
    for (i=0;i < hooks_cnt;i++) {
	const struct cli_bc *bc = &engine->bcs.all_bcs[hooks[i]];
	if (bc->lsig) {
	    if (!cctx->hook_lsig_matches ||
		!cli_bitset_test(cctx->hook_lsig_matches, bc->hook_lsig_id-1))
		continue;
	    cli_dbgmsg("Bytecode: executing bytecode %u (lsig matched)\n" , bc->id);
	}
	cli_bytecode_context_setfuncid(ctx, bc, 0);
	ret = cli_bytecode_run(&engine->bcs, bc, ctx);
	executed++;
	if (ret != CL_SUCCESS) {
	    cli_warnmsg("Bytecode %u failed to run: %s\n", bc->id, cl_strerror(ret));
	    errorflag = 1;
	    continue;
	}
	if (ctx->virname) {
	    cli_dbgmsg("Bytecode found virus: %s\n", ctx->virname);
	    if (virname)
		*virname = ctx->virname;
	    cli_bytecode_context_clear(ctx);
	    return CL_VIRUS;
	}
	ret = cli_bytecode_context_getresult_int(ctx);
	/* TODO: use prefix here */
	cli_dbgmsg("Bytecode %u returned %u\n", bc->id, ret);
	if (ret == 0xcea5e) {
	    cli_dbgmsg("Bytecode set BREAK flag in hook!\n");
	    breakflag = 1;
	}
	if (!ret) {
	    char *tempfile;
	    int fd = cli_bytecode_context_getresult_file(ctx, &tempfile);
	    if (fd && fd != -1) {
		if (cctx && cctx->engine->keeptmp)
		    cli_dbgmsg("Bytecode %u unpacked file saved in %s\n",
			       bc->id, tempfile);
		else
		    cli_dbgmsg("Bytecode %u unpacked file\n", bc->id);
		lseek(fd, 0, SEEK_SET);
		cli_dbgmsg("***** Scanning unpacked file ******\n");
		cctx->recursion++;
		ret = cli_magic_scandesc(fd, cctx);
		cctx->recursion--;
		if (!cctx || !cctx->engine->keeptmp)
		    if (ftruncate(fd, 0) == -1)
			cli_dbgmsg("ftruncate failed on %d\n", fd);
		close(fd);
		if (!cctx || !cctx->engine->keeptmp) {
		    if (tempfile && cli_unlink(tempfile))
			ret = CL_EUNLINK;
		}
		free(tempfile);
		if (ret != CL_CLEAN) {
		    if (ret == CL_VIRUS)
			cli_dbgmsg("Scanning unpacked file by bytecode %u found a virus\n", bc->id);
		    cli_bytecode_context_clear(ctx);
		    return ret;
		}
		cli_bytecode_context_reset(ctx);
		continue;
	    }
	}
	cli_bytecode_context_reset(ctx);
    }
    if (executed)
	cli_dbgmsg("Bytecode: executed %u bytecodes for this hook\n", executed);
    else
	cli_dbgmsg("Bytecode: no logical signature matched, no bytecode executed\n");
    if (errorflag && cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
	return CL_EBYTECODE_TESTFAIL;
    return breakflag ? CL_BREAK : CL_CLEAN;
}


net: introduce qemu_receive_packet()

Some NIC supports loopback mode and this is done by calling
nc->info->receive() directly which in fact suppresses the effort of
reentrancy check that is done in qemu_net_queue_send().

Unfortunately we can't use qemu_net_queue_send() here since for
loopback there's no sender as peer, so this patch introduce a
qemu_receive_packet() which is used for implementing loopback mode
for a NIC with this check.

NIC that supports loopback mode will be converted to this helper.

This is intended to address CVE-2021-3416.

Cc: Prasad J Pandit <ppandit@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Jason Wang <jasowang@redhat.com>

int qemu_can_send_packet(NetClientState *sender)
{
    int vm_running = runstate_is_running();

    if (!vm_running) {
        return 0;
    }

    if (!sender->peer) {
        return 1;
    }

    if (sender->peer->receive_disabled) {
        return 0;
    } else if (sender->peer->info->can_receive &&
               !sender->peer->info->can_receive(sender->peer)) {
        return 0;
    }
    return 1;
}

A potential stack overflow via infinite loop issue was found in various NIC emulators of QEMU in versions up to and including 5.2.0. The issue occurs in loopback mode of a NIC wherein reentrant DMA checks get bypassed. A guest user/process may use this flaw to consume CPU cycles or crash the QEMU process on the host resulting in DoS scenario.


int qemu_can_send_packet(NetClientState *sender)
{
    int vm_running = runstate_is_running();

    if (!vm_running) {
        return 0;
    }

    if (!sender->peer) {
        return 1;
    }

    return qemu_can_receive_packet(sender->peer);
}


file-xwd: sanity check # of colors and map entries (CVE-2013-1978)

The number of colors in an image shouldn't be higher than the number of
colormap entries. Additionally, consolidate post error cleanup in
load_image().

load_image (const gchar  *filename,
            GError      **error)
{
  FILE            *ifp;
  gint             depth, bpp;
  gint32           image_ID;
  L_XWDFILEHEADER  xwdhdr;
  L_XWDCOLOR      *xwdcolmap = NULL;

  ifp = g_fopen (filename, "rb");
  if (!ifp)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
      return -1;
    }

  read_xwd_header (ifp, &xwdhdr);
  if (xwdhdr.l_file_version != 7)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Could not read XWD header from '%s'"),
                   gimp_filename_to_utf8 (filename));
      fclose (ifp);
      return -1;
    }

#ifdef XWD_COL_WAIT_DEBUG
  {
    int k = 1;

    while (k)
      k = k;
  }
#endif

  /* Position to start of XWDColor structures */
  fseek (ifp, (long)xwdhdr.l_header_size, SEEK_SET);

  /* Guard against insanely huge color maps -- gimp_image_set_colormap() only
   * accepts colormaps with 0..256 colors anyway. */
  if (xwdhdr.l_colormap_entries > 256)
    {
      g_message (_("'%s':\nIllegal number of colormap entries: %ld"),
                 gimp_filename_to_utf8 (filename),
                 (long)xwdhdr.l_colormap_entries);
      fclose (ifp);
      return -1;
    }

  if (xwdhdr.l_colormap_entries > 0)
    {
      xwdcolmap = g_new (L_XWDCOLOR, xwdhdr.l_colormap_entries);

      read_xwd_cols (ifp, &xwdhdr, xwdcolmap);

#ifdef XWD_COL_DEBUG
      {
        int j;
        printf ("File %s\n",filename);
        for (j = 0; j < xwdhdr.l_colormap_entries; j++)
          printf ("Entry 0x%08lx: 0x%04lx,  0x%04lx, 0x%04lx, %d\n",
                  (long)xwdcolmap[j].l_pixel,(long)xwdcolmap[j].l_red,
                  (long)xwdcolmap[j].l_green,(long)xwdcolmap[j].l_blue,
                  (int)xwdcolmap[j].l_flags);
      }
#endif

      if (xwdhdr.l_file_version != 7)
        {
          g_message (_("Can't read color entries"));
          g_free (xwdcolmap);
          fclose (ifp);
          return (-1);
        }
    }

  if (xwdhdr.l_pixmap_width <= 0)
    {
      g_message (_("'%s':\nNo image width specified"),
                 gimp_filename_to_utf8 (filename));
      g_free (xwdcolmap);
      fclose (ifp);
      return (-1);
    }

  if (xwdhdr.l_pixmap_width > GIMP_MAX_IMAGE_SIZE
      || xwdhdr.l_bytes_per_line > GIMP_MAX_IMAGE_SIZE * 3)
    {
      g_message (_("'%s':\nImage width is larger than GIMP can handle"),
                 gimp_filename_to_utf8 (filename));
      g_free (xwdcolmap);
      fclose (ifp);
      return (-1);
    }

  if (xwdhdr.l_pixmap_height <= 0)
    {
      g_message (_("'%s':\nNo image height specified"),
                 gimp_filename_to_utf8 (filename));
      g_free (xwdcolmap);
      fclose (ifp);
      return (-1);
    }

  if (xwdhdr.l_pixmap_height > GIMP_MAX_IMAGE_SIZE)
    {
      g_message (_("'%s':\nImage height is larger than GIMP can handle"),
                 gimp_filename_to_utf8 (filename));
      g_free (xwdcolmap);
      fclose (ifp);
      return (-1);
    }

  gimp_progress_init_printf (_("Opening '%s'"),
                             gimp_filename_to_utf8 (filename));

  depth = xwdhdr.l_pixmap_depth;
  bpp   = xwdhdr.l_bits_per_pixel;

  image_ID = -1;
  switch (xwdhdr.l_pixmap_format)
    {
    case 0:    /* Single plane bitmap */
      if ((depth == 1) && (bpp == 1))
        { /* Can be performed by format 2 loader */
          image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      break;

    case 1:    /* Single plane pixmap */
      if ((depth <= 24) && (bpp == 1))
        {
          image_ID = load_xwd_f1_d24_b1 (filename, ifp, &xwdhdr, xwdcolmap,
                                         error);
        }
      break;

    case 2:    /* Multiplane pixmaps */
      if ((depth == 1) && (bpp == 1))
        {
          image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      else if ((depth <= 8) && (bpp == 8))
        {
          image_ID = load_xwd_f2_d8_b8 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      else if ((depth <= 16) && (bpp == 16))
        {
          image_ID = load_xwd_f2_d16_b16 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      else if ((depth <= 24) && ((bpp == 24) || (bpp == 32)))
        {
          image_ID = load_xwd_f2_d24_b32 (filename, ifp, &xwdhdr, xwdcolmap,
                                          error);
        }
      else if ((depth <= 32) && (bpp == 32))
        {
          image_ID = load_xwd_f2_d32_b32 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      break;
    }
  gimp_progress_update (1.0);

  fclose (ifp);

  if (xwdcolmap)
    g_free (xwdcolmap);

  if (image_ID == -1 && ! (error && *error))
    g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                 _("XWD-file %s has format %d, depth %d and bits per pixel %d. "
                   "Currently this is not supported."),
                 gimp_filename_to_utf8 (filename),
                 (gint) xwdhdr.l_pixmap_format, depth, bpp);

  return image_ID;
}

Heap-based buffer overflow in the read_xwd_cols function in file-xwd.c in the X Window Dump (XWD) plug-in in GIMP 2.6.9 and earlier allows remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via an X Window System (XWD) image dump with more colors than color map entries.


load_image (const gchar  *filename,
            GError      **error)
{
  FILE            *ifp = NULL;
  gint             depth, bpp;
  gint32           image_ID = -1;
  L_XWDFILEHEADER  xwdhdr;
  L_XWDCOLOR      *xwdcolmap = NULL;

  ifp = g_fopen (filename, "rb");
  if (!ifp)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
      goto out;
    }

  read_xwd_header (ifp, &xwdhdr);
  if (xwdhdr.l_file_version != 7)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Could not read XWD header from '%s'"),
                   gimp_filename_to_utf8 (filename));
      goto out;
    }

#ifdef XWD_COL_WAIT_DEBUG
  {
    int k = 1;

    while (k)
      k = k;
  }
#endif

  /* Position to start of XWDColor structures */
  fseek (ifp, (long)xwdhdr.l_header_size, SEEK_SET);

  /* Guard against insanely huge color maps -- gimp_image_set_colormap() only
   * accepts colormaps with 0..256 colors anyway. */
  if (xwdhdr.l_colormap_entries > 256)
    {
      g_message (_("'%s':\nIllegal number of colormap entries: %ld"),
                 gimp_filename_to_utf8 (filename),
                 (long)xwdhdr.l_colormap_entries);
      goto out;
    }

  if (xwdhdr.l_colormap_entries > 0)
    {
      if (xwdhdr.l_colormap_entries < xwdhdr.l_ncolors)
        {
          g_message (_("'%s':\nNumber of colormap entries < number of colors"),
                     gimp_filename_to_utf8 (filename));
          goto out;
        }

      xwdcolmap = g_new (L_XWDCOLOR, xwdhdr.l_colormap_entries);

      read_xwd_cols (ifp, &xwdhdr, xwdcolmap);

#ifdef XWD_COL_DEBUG
      {
        int j;
        printf ("File %s\n",filename);
        for (j = 0; j < xwdhdr.l_colormap_entries; j++)
          printf ("Entry 0x%08lx: 0x%04lx,  0x%04lx, 0x%04lx, %d\n",
                  (long)xwdcolmap[j].l_pixel,(long)xwdcolmap[j].l_red,
                  (long)xwdcolmap[j].l_green,(long)xwdcolmap[j].l_blue,
                  (int)xwdcolmap[j].l_flags);
      }
#endif

      if (xwdhdr.l_file_version != 7)
        {
          g_message (_("Can't read color entries"));
          goto out;
        }
    }

  if (xwdhdr.l_pixmap_width <= 0)
    {
      g_message (_("'%s':\nNo image width specified"),
                 gimp_filename_to_utf8 (filename));
      goto out;
    }

  if (xwdhdr.l_pixmap_width > GIMP_MAX_IMAGE_SIZE
      || xwdhdr.l_bytes_per_line > GIMP_MAX_IMAGE_SIZE * 3)
    {
      g_message (_("'%s':\nImage width is larger than GIMP can handle"),
                 gimp_filename_to_utf8 (filename));
      goto out;
    }

  if (xwdhdr.l_pixmap_height <= 0)
    {
      g_message (_("'%s':\nNo image height specified"),
                 gimp_filename_to_utf8 (filename));
      goto out;
    }

  if (xwdhdr.l_pixmap_height > GIMP_MAX_IMAGE_SIZE)
    {
      g_message (_("'%s':\nImage height is larger than GIMP can handle"),
                 gimp_filename_to_utf8 (filename));
      goto out;
    }

  gimp_progress_init_printf (_("Opening '%s'"),
                             gimp_filename_to_utf8 (filename));

  depth = xwdhdr.l_pixmap_depth;
  bpp   = xwdhdr.l_bits_per_pixel;

  image_ID = -1;
  switch (xwdhdr.l_pixmap_format)
    {
    case 0:    /* Single plane bitmap */
      if ((depth == 1) && (bpp == 1))
        { /* Can be performed by format 2 loader */
          image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      break;

    case 1:    /* Single plane pixmap */
      if ((depth <= 24) && (bpp == 1))
        {
          image_ID = load_xwd_f1_d24_b1 (filename, ifp, &xwdhdr, xwdcolmap,
                                         error);
        }
      break;

    case 2:    /* Multiplane pixmaps */
      if ((depth == 1) && (bpp == 1))
        {
          image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      else if ((depth <= 8) && (bpp == 8))
        {
          image_ID = load_xwd_f2_d8_b8 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      else if ((depth <= 16) && (bpp == 16))
        {
          image_ID = load_xwd_f2_d16_b16 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      else if ((depth <= 24) && ((bpp == 24) || (bpp == 32)))
        {
          image_ID = load_xwd_f2_d24_b32 (filename, ifp, &xwdhdr, xwdcolmap,
                                          error);
        }
      else if ((depth <= 32) && (bpp == 32))
        {
          image_ID = load_xwd_f2_d32_b32 (filename, ifp, &xwdhdr, xwdcolmap);
        }
      break;
    }
  gimp_progress_update (1.0);

  if (image_ID == -1 && ! (error && *error))
    g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                 _("XWD-file %s has format %d, depth %d and bits per pixel %d. "
                   "Currently this is not supported."),
                 gimp_filename_to_utf8 (filename),
                 (gint) xwdhdr.l_pixmap_format, depth, bpp);

out:
  if (ifp)
    {
      fclose (ifp);
    }

  if (xwdcolmap)
    {
      g_free (xwdcolmap);
    }

  return image_ID;
}


AVA_Sort( LDAPRDN rdn, int nAVAs )
{
	LDAPAVA	*ava_i;
	int		i;

	assert( rdn != NULL );

	for ( i = 1; i < nAVAs; i++ ) {
		LDAPAVA *ava_j;
		int j;

		ava_i = rdn[ i ];
		for ( j = i-1; j >=0; j-- ) {
			int a;

			ava_j = rdn[ j ];
			a = strcmp( ava_i->la_attr.bv_val, ava_j->la_attr.bv_val );

			/* RFC4512 does not allow multiple AVAs
			 * with the same attribute type in RDN (ITS#5968) */
			if ( a == 0 )
				return LDAP_INVALID_DN_SYNTAX;

			if ( a > 0 )
				break;

			rdn[ j+1 ] = rdn[ j ];
		}
		rdn[ j+1 ] = ava_i;
	}
	return LDAP_SUCCESS;
}

A flaw was discovered in OpenLDAP before 2.4.57 leading to an invalid pointer free and slapd crash in the saslAuthzTo processing, resulting in denial of service.


AVA_Sort( LDAPRDN rdn, int nAVAs )
{
	LDAPAVA	*ava_i;
	int		i;
	int		rc = LDAP_SUCCESS;

	assert( rdn != NULL );

	for ( i = 1; i < nAVAs; i++ ) {
		LDAPAVA *ava_j;
		int j;

		ava_i = rdn[ i ];
		for ( j = i-1; j >=0; j-- ) {
			int a;

			ava_j = rdn[ j ];
			a = strcmp( ava_i->la_attr.bv_val, ava_j->la_attr.bv_val );

			/* RFC4512 does not allow multiple AVAs
			 * with the same attribute type in RDN (ITS#5968) */
			if ( a == 0 )
				rc = LDAP_INVALID_DN_SYNTAX;

			if ( a > 0 )
				break;

			rdn[ j+1 ] = rdn[ j ];
		}
		rdn[ j+1 ] = ava_i;
	}
	return rc;
}


USB: rio500: refuse more than one device at a time

This driver is using a global variable. It cannot handle more than
one device at a time. The issue has been existing since the dawn
of the driver.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
Reported-by: syzbot+35f04d136fc975a70da4@syzkaller.appspotmail.com
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

static int probe_rio(struct usb_interface *intf,
		     const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	struct rio_usb_data *rio = &rio_instance;
	int retval;

	dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);

	retval = usb_register_dev(intf, &usb_rio_class);
	if (retval) {
		dev_err(&dev->dev,
			"Not able to get a minor for this device.\n");
		return -ENOMEM;
	}

	rio->rio_dev = dev;

	if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
		dev_err(&dev->dev,
			"probe_rio: Not enough memory for the output buffer\n");
		usb_deregister_dev(intf, &usb_rio_class);
		return -ENOMEM;
	}
	dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf);

	if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) {
		dev_err(&dev->dev,
			"probe_rio: Not enough memory for the input buffer\n");
		usb_deregister_dev(intf, &usb_rio_class);
		kfree(rio->obuf);
		return -ENOMEM;
	}
	dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf);

	mutex_init(&(rio->lock));

	usb_set_intfdata (intf, rio);
	rio->present = 1;

	return 0;
}

An issue was discovered in the Linux kernel before 5.1.8. There is a double-free caused by a malicious USB device in the drivers/usb/misc/rio500.c driver.


static int probe_rio(struct usb_interface *intf,
		     const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	struct rio_usb_data *rio = &rio_instance;
	int retval = 0;

	mutex_lock(&rio500_mutex);
	if (rio->present) {
		dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum);
		retval = -EBUSY;
		goto bail_out;
	} else {
		dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
	}

	retval = usb_register_dev(intf, &usb_rio_class);
	if (retval) {
		dev_err(&dev->dev,
			"Not able to get a minor for this device.\n");
		retval = -ENOMEM;
		goto bail_out;
	}

	rio->rio_dev = dev;

	if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
		dev_err(&dev->dev,
			"probe_rio: Not enough memory for the output buffer\n");
		usb_deregister_dev(intf, &usb_rio_class);
		retval = -ENOMEM;
		goto bail_out;
	}
	dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf);

	if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) {
		dev_err(&dev->dev,
			"probe_rio: Not enough memory for the input buffer\n");
		usb_deregister_dev(intf, &usb_rio_class);
		kfree(rio->obuf);
		retval = -ENOMEM;
		goto bail_out;
	}
	dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf);

	mutex_init(&(rio->lock));

	usb_set_intfdata (intf, rio);
	rio->present = 1;
bail_out:
	mutex_unlock(&rio500_mutex);

	return retval;
}