public inbox for ltp@lists.linux.it
 help / color / mirror / Atom feed
From: Petr Vorel <pvorel@suse.cz>
To: Martin Doucha <mdoucha@suse.cz>
Cc: ltp@lists.linux.it
Subject: Re: [LTP] [PATCH v3 4/4] Add test for CVE 2023-31248
Date: Tue, 21 Nov 2023 14:19:38 +0100	[thread overview]
Message-ID: <20231121131938.GA121089@pevik> (raw)
In-Reply-To: <20231116164723.4012-5-mdoucha@suse.cz>

Hi Martin, Souta,

nice work, few very minor nits below.

> Fixes #1058

> Signed-off-by: Martin Doucha <mdoucha@suse.cz>
> Co-Developed-by: Souta Kawahara <souta.kawahara@miraclelinux.com>
> ---

> Changes since v1: New patch
> Changes since v2:
> - Use netfilter GOTO rule jumping into the deleted chain
> - Check for ENOENT error instead of kernel taint

> The test does not use any external utilities so I've decided not to add it
> to the net.tcp_cmds runfile.

Sure.

>  runtest/cve                           |   1 +
>  testcases/network/iptables/.gitignore |   1 +
>  testcases/network/iptables/Makefile   |   2 +-
>  testcases/network/iptables/nft02.c    | 213 ++++++++++++++++++++++++++
>  4 files changed, 216 insertions(+), 1 deletion(-)
>  create mode 100644 testcases/network/iptables/.gitignore
>  create mode 100644 testcases/network/iptables/nft02.c

> diff --git a/runtest/cve b/runtest/cve
> index 569558af2..1d1d87597 100644
> --- a/runtest/cve
> +++ b/runtest/cve
> @@ -86,6 +86,7 @@ cve-2022-2590 dirtyc0w_shmem
>  cve-2022-23222 bpf_prog07
>  cve-2023-1829 tcindex01
>  cve-2023-0461 setsockopt10
> +cve-2023-31248 nft02
>  # Tests below may cause kernel memory leak
>  cve-2020-25704 perf_event_open03
>  cve-2022-0185 fsconfig03
> diff --git a/testcases/network/iptables/.gitignore b/testcases/network/iptables/.gitignore
> new file mode 100644
> index 000000000..0f47a7313
> --- /dev/null
> +++ b/testcases/network/iptables/.gitignore
> @@ -0,0 +1 @@
> +nft02
> diff --git a/testcases/network/iptables/Makefile b/testcases/network/iptables/Makefile
> index 1b42f25db..02e228cbc 100644
> --- a/testcases/network/iptables/Makefile
> +++ b/testcases/network/iptables/Makefile
> @@ -5,7 +5,7 @@

>  top_srcdir		?= ../../..

> -include $(top_srcdir)/include/mk/env_pre.mk
> +include $(top_srcdir)/include/mk/testcases.mk

>  INSTALL_TARGETS		:= *.sh

> diff --git a/testcases/network/iptables/nft02.c b/testcases/network/iptables/nft02.c
> new file mode 100644
> index 000000000..a6e795af3
> --- /dev/null
> +++ b/testcases/network/iptables/nft02.c
> @@ -0,0 +1,213 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2023 SUSE LLC
> + * Author: Marcos Paulo de Souza <mpdesouza@suse.com>
> + * LTP port: Martin Doucha <mdoucha@suse.cz>
> + */
> +
> +/*\
We usually add [Description] here. Although it looks bogus to me, I can add it
before merge.

> + * CVE-2023-31248
> + *
> + * Test for use-after-free when adding a new rule to a chain deleted
> + * in the same netlink message batch.
> + *
> + * Kernel bug fixed in:
> + *
> + *  commit 515ad530795c118f012539ed76d02bacfd426d89
> + *  Author: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
> + *  Date:   Wed Jul 5 09:12:55 2023 -0300
> + *
> + *  netfilter: nf_tables: do not ignore genmask when looking up chain by id
> + */
> +
> +#include <linux/netlink.h>
> +#include <linux/tcp.h>
> +#include <arpa/inet.h>
> +#include <linux/netfilter.h>
> +#include "lapi/nf_tables.h"
> +#include <linux/netfilter/nfnetlink.h>
> +#include "tst_test.h"
> +#include "tst_netlink.h"
> +
> +#define TABNAME "ltp_table1"
> +#define SRCCHAIN "ltp_chain_src"
> +#define DESTCHAIN "ltp_chain_dest"
> +
> +static uint32_t chain_id;
> +static uint32_t imm_dreg, imm_verdict;
> +static struct tst_netlink_context *ctx;
> +
> +/* Table creation config */
> +static const struct tst_netlink_attr_list table_config[] = {
> +	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
> +	{0, NULL, -1, NULL}
> +};
> +
> +/* Chain creation and deletion config */
> +static const struct tst_netlink_attr_list destchain_config[] = {
> +	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
> +	{NFTA_CHAIN_NAME, DESTCHAIN, strlen(DESTCHAIN) + 1, NULL},
> +	{NFTA_CHAIN_ID, &chain_id, sizeof(chain_id), NULL},
> +	{0, NULL, -1, NULL}
> +};
> +
> +static const struct tst_netlink_attr_list delchain_config[] = {
> +	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
> +	{NFTA_CHAIN_NAME, DESTCHAIN, strlen(DESTCHAIN) + 1, NULL},
> +	{0, NULL, -1, NULL}
> +};
> +
> +static const struct tst_netlink_attr_list srcchain_config[] = {
> +	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
> +	{NFTA_CHAIN_NAME, SRCCHAIN, strlen(SRCCHAIN) + 1, NULL},
> +	{0, NULL, -1, NULL}
> +};
> +
> +/* Rule creation config */
> +static const struct tst_netlink_attr_list rule_verdict_config[] = {
> +	{NFTA_VERDICT_CODE, &imm_verdict, sizeof(imm_verdict), NULL},
> +	{NFTA_VERDICT_CHAIN_ID, &chain_id, sizeof(chain_id), NULL},
> +	{0, NULL, -1, NULL}
> +};
> +
> +static const struct tst_netlink_attr_list rule_data_config[] = {
> +	{NFTA_IMMEDIATE_DREG, &imm_dreg, sizeof(imm_dreg), NULL},
> +	{NFTA_IMMEDIATE_DATA, NULL, 0, (const struct tst_netlink_attr_list[]) {
> +		{NFTA_DATA_VERDICT, NULL, 0, rule_verdict_config},
> +		{0, NULL, -1, NULL}
> +	}},
> +	{0, NULL, -1, NULL}
> +};
> +
> +static const struct tst_netlink_attr_list rule_expr_config[] = {
> +	{NFTA_LIST_ELEM, NULL, 0, (const struct tst_netlink_attr_list[]) {
> +		{NFTA_EXPR_NAME, "immediate", 10, NULL},
> +		{NFTA_EXPR_DATA, NULL, 0, rule_data_config},
> +		{0, NULL, -1, NULL}
> +	}},
> +	{0, NULL, -1, NULL}
> +};
> +
> +static const struct tst_netlink_attr_list rule_config[] = {
> +	{NFTA_RULE_EXPRESSIONS, NULL, 0, rule_expr_config},
> +	{NFTA_RULE_TABLE, TABNAME, strlen(TABNAME) + 1, NULL},
> +	{NFTA_RULE_CHAIN, SRCCHAIN, strlen(SRCCHAIN) + 1, NULL},
> +	{0, NULL, -1, NULL}
> +};
> +
> +static void setup(void)
> +{
> +	tst_setup_netns();
> +
> +	chain_id = htonl(77);
nit: Although it's obvious that ID chain_id is some random number I would define
77 also above.

> +	imm_dreg = htonl(NFT_REG_VERDICT);
> +	imm_verdict = htonl(NFT_GOTO);
> +}
> +
> +static void run(void)
> +{
> +	int ret;
> +	struct nlmsghdr header;
> +	struct nfgenmsg nfpayload;
> +
> +	memset(&header, 0, sizeof(header));
> +	memset(&nfpayload, 0, sizeof(nfpayload));
> +	nfpayload.version = NFNETLINK_V0;
> +
> +	ctx = NETLINK_CREATE_CONTEXT(NETLINK_NETFILTER);
> +
> +	/* Start netfilter batch */
> +	header.nlmsg_type = NFNL_MSG_BATCH_BEGIN;
> +	header.nlmsg_flags = NLM_F_REQUEST;
> +	nfpayload.nfgen_family = AF_UNSPEC;
> +	nfpayload.res_id = htons(NFNL_SUBSYS_NFTABLES);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +
> +	/* Add table */
> +	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWTABLE;
> +	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
> +	nfpayload.nfgen_family = NFPROTO_IPV4;
> +	nfpayload.res_id = htons(0);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +	NETLINK_ADD_ATTR_LIST(ctx, table_config);
> +
> +	/* Add destination chain */
> +	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWCHAIN;
> +	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
> +	nfpayload.nfgen_family = NFPROTO_IPV4;
> +	nfpayload.res_id = htons(0);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +	NETLINK_ADD_ATTR_LIST(ctx, destchain_config);
> +
> +	/* Delete destination chain */
> +	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_DELCHAIN;
> +	header.nlmsg_flags = NLM_F_REQUEST;
> +	nfpayload.nfgen_family = NFPROTO_IPV4;
> +	nfpayload.res_id = htons(0);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +	NETLINK_ADD_ATTR_LIST(ctx, delchain_config);
> +
> +	/* Add destination chain */
nit: this looks to be source chain
Out of curriosity I'm looking at the reproducer
(https://bugzilla.suse.com/attachment.cgi?id=868806)
and it needs just single chain.
But test needs both for some reason.
Anyway, nice work, kernel oops printed to dmesg on older kernel.

Reviewed-by: Petr Vorel <pvorel@suse.cz>
Tested-by: Petr Vorel <pvorel@suse.cz>

> +	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWCHAIN;
> +	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
> +	nfpayload.nfgen_family = NFPROTO_IPV4;
> +	nfpayload.res_id = htons(0);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +	NETLINK_ADD_ATTR_LIST(ctx, srcchain_config);
> +
> +	/* Add rule to source chain. Require ACK and check for ENOENT error. */
> +	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWRULE;
> +	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_APPEND | NLM_F_CREATE |
> +		NLM_F_ACK;
> +	nfpayload.nfgen_family = NFPROTO_IPV4;
> +	nfpayload.res_id = htons(0);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +	NETLINK_ADD_ATTR_LIST(ctx, rule_config);
> +
> +	/* End batch */
> +	header.nlmsg_type = NFNL_MSG_BATCH_END;
> +	header.nlmsg_flags = NLM_F_REQUEST;
> +	nfpayload.nfgen_family = AF_UNSPEC;
> +	nfpayload.res_id = htons(NFNL_SUBSYS_NFTABLES);
> +	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
> +
> +	ret = NETLINK_SEND_VALIDATE(ctx);
> +	TST_ERR = tst_netlink_errno;
> +	NETLINK_DESTROY_CONTEXT(ctx);
> +	ctx = NULL;
> +
> +	if (ret)
> +		tst_res(TFAIL, "Netfilter chain list is corrupted");
> +	else if (TST_ERR == ENOENT)
> +		tst_res(TPASS, "Deleted netfilter chain cannot be referenced");
> +	else if (TST_ERR == EOPNOTSUPP || TST_ERR == EINVAL)
> +		tst_brk(TCONF, "Test requires unavailable netfilter features");
> +	else
> +		tst_brk(TBROK | TTERRNO, "Unknown nfnetlink error");
> +}
> +
> +static void cleanup(void)
> +{
> +	NETLINK_DESTROY_CONTEXT(ctx);
> +}
> +
> +static struct tst_test test = {
> +	.test_all = run,
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.taint_check = TST_TAINT_W | TST_TAINT_D,
> +	.needs_kconfigs = (const char *[]) {
> +		"CONFIG_USER_NS=y",
> +		"CONFIG_NF_TABLES",
> +		NULL
> +	},
> +	.save_restore = (const struct tst_path_val[]) {
> +		{"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP},
Out of curiosity, why this?

CVE mentions "Exploiting it requires CAP_NET_ADMIN in any user or network
namespace.", but how is it related to changing max_user_namespaces value?

Also, vulnerable kernel reproducers with any max_user_namespaces value, or
without setting max_user_namespaces at all.

I can fix all the typos (only) before merge or you send v4 (whatever you prefer).

Kind regards,
Petr

> +		{}
> +	},
> +	.tags = (const struct tst_tag[]) {
> +		{"linux-git", "515ad530795c"},
> +		{"CVE", "2023-31248"},
> +		{}
> +	}
> +};

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

  reply	other threads:[~2023-11-21 13:19 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-16 16:46 [LTP] [PATCH v3 0/4] Test for CVE 2023-31248 Martin Doucha
2023-11-16 16:46 ` [LTP] [PATCH v3 1/4] tst_netlink: Add helper functions for handling generic attributes Martin Doucha
2023-11-21 10:41   ` Petr Vorel
2023-11-16 16:46 ` [LTP] [PATCH v3 2/4] tst_netlink_check_acks(): Stop on first error regardless of ACK request Martin Doucha
2023-11-21 12:13   ` Petr Vorel
2023-11-16 16:46 ` [LTP] [PATCH v3 3/4] Add lapi/nf_tables.h Martin Doucha
2023-11-21 10:10   ` Petr Vorel
2023-11-21 10:23     ` Martin Doucha
2023-11-16 16:46 ` [LTP] [PATCH v3 4/4] Add test for CVE 2023-31248 Martin Doucha
2023-11-21 13:19   ` Petr Vorel [this message]
2023-11-21 14:25     ` Martin Doucha

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231121131938.GA121089@pevik \
    --to=pvorel@suse.cz \
    --cc=ltp@lists.linux.it \
    --cc=mdoucha@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox