Commit 5288993c authored by Chuck Lever's avatar Chuck Lever
Browse files

xdrgen: Add enum value validation to generated decoders



XDR enum decoders generated by xdrgen do not verify that incoming
values are valid members of the enum. Incoming out-of-range values
from malicious or buggy peers propagate through the system
unchecked.

Add validation logic to generated enum decoders using a switch
statement that explicitly lists valid enumerator values. The
compiler optimizes this to a simple range check when enum values
are dense (contiguous), while correctly rejecting invalid values
for sparse enums with gaps in their value ranges.

The --no-enum-validation option on the source subcommand disables
this validation when not needed.

The minimum and maximum fields in _XdrEnum, which were previously
unused placeholders for a range-based validation approach, have
been removed since the switch-based validation handles both dense
and sparse enums correctly.

Because the new mechanism results in substantive changes to
generated code, existing .x files are regenerated. Unrelated white
space and semicolon changes in the generated code are due to recent
commit 1c873a2f ("xdrgen: Don't generate unnecessary semicolon")
and commit 38c4df91242b ("xdrgen: Address some checkpatch whitespace
complaints").

Reviewed-by: default avatarNeilBrown <neil@brown.name>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 4c53b890
Loading
Loading
Loading
Loading
+86 −19
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x
// XDR specification modification time: Mon Oct 14 09:10:13 2024
// XDR specification modification time: Thu Dec 25 13:44:43 2025

#include <linux/sunrpc/svc.h>

@@ -11,13 +11,13 @@ static bool __maybe_unused
xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr)
{
	return xdrgen_decode_hyper(xdr, ptr);
};
}

static bool __maybe_unused
xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr)
{
	return xdrgen_decode_unsigned_int(xdr, ptr);
};
}

static bool __maybe_unused
xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
@@ -28,7 +28,7 @@ xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr)
		if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i]))
			return false;
	return true;
};
}

static bool __maybe_unused
xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
@@ -38,13 +38,13 @@ xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr)
	if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds))
		return false;
	return true;
};
}

static bool __maybe_unused
xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr)
{
	return xdrgen_decode_bool(xdr, ptr);
};
}

static bool __maybe_unused
xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr)
@@ -60,7 +60,7 @@ xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *pt
	if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode))
		return false;
	return true;
};
}

static bool __maybe_unused
xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 *ptr)
@@ -69,6 +69,15 @@ xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_ac

	if (xdr_stream_decode_u32(xdr, &val) < 0)
		return false;
	/* Compiler may optimize to a range check for dense enums */
	switch (val) {
	case OPEN_ARGS_SHARE_ACCESS_READ:
	case OPEN_ARGS_SHARE_ACCESS_WRITE:
	case OPEN_ARGS_SHARE_ACCESS_BOTH:
		break;
	default:
		return false;
	}
	*ptr = val;
	return true;
}
@@ -80,6 +89,16 @@ xdrgen_decode_open_args_share_deny4(struct xdr_stream *xdr, open_args_share_deny

	if (xdr_stream_decode_u32(xdr, &val) < 0)
		return false;
	/* Compiler may optimize to a range check for dense enums */
	switch (val) {
	case OPEN_ARGS_SHARE_DENY_NONE:
	case OPEN_ARGS_SHARE_DENY_READ:
	case OPEN_ARGS_SHARE_DENY_WRITE:
	case OPEN_ARGS_SHARE_DENY_BOTH:
		break;
	default:
		return false;
	}
	*ptr = val;
	return true;
}
@@ -91,6 +110,19 @@ xdrgen_decode_open_args_share_access_want4(struct xdr_stream *xdr, open_args_sha

	if (xdr_stream_decode_u32(xdr, &val) < 0)
		return false;
	/* Compiler may optimize to a range check for dense enums */
	switch (val) {
	case OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG:
	case OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG:
	case OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL:
	case OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL:
	case OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED:
	case OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS:
	case OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION:
		break;
	default:
		return false;
	}
	*ptr = val;
	return true;
}
@@ -102,6 +134,19 @@ xdrgen_decode_open_args_open_claim4(struct xdr_stream *xdr, open_args_open_claim

	if (xdr_stream_decode_u32(xdr, &val) < 0)
		return false;
	/* Compiler may optimize to a range check for dense enums */
	switch (val) {
	case OPEN_ARGS_OPEN_CLAIM_NULL:
	case OPEN_ARGS_OPEN_CLAIM_PREVIOUS:
	case OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR:
	case OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV:
	case OPEN_ARGS_OPEN_CLAIM_FH:
	case OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH:
	case OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH:
		break;
	default:
		return false;
	}
	*ptr = val;
	return true;
}
@@ -113,6 +158,16 @@ xdrgen_decode_open_args_createmode4(struct xdr_stream *xdr, open_args_createmode

	if (xdr_stream_decode_u32(xdr, &val) < 0)
		return false;
	/* Compiler may optimize to a range check for dense enums */
	switch (val) {
	case OPEN_ARGS_CREATEMODE_UNCHECKED4:
	case OPEN_ARGS_CREATE_MODE_GUARDED:
	case OPEN_ARGS_CREATEMODE_EXCLUSIVE4:
	case OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1:
		break;
	default:
		return false;
	}
	*ptr = val;
	return true;
}
@@ -121,19 +176,19 @@ bool
xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr)
{
	return xdrgen_decode_open_arguments4(xdr, ptr);
};
}

bool
xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr)
{
	return xdrgen_decode_nfstime4(xdr, ptr);
};
}

bool
xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr)
{
	return xdrgen_decode_nfstime4(xdr, ptr);
};
}

static bool __maybe_unused
xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr)
@@ -142,6 +197,18 @@ xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type

	if (xdr_stream_decode_u32(xdr, &val) < 0)
		return false;
	/* Compiler may optimize to a range check for dense enums */
	switch (val) {
	case OPEN_DELEGATE_NONE:
	case OPEN_DELEGATE_READ:
	case OPEN_DELEGATE_WRITE:
	case OPEN_DELEGATE_NONE_EXT:
	case OPEN_DELEGATE_READ_ATTRS_DELEG:
	case OPEN_DELEGATE_WRITE_ATTRS_DELEG:
		break;
	default:
		return false;
	}
	*ptr = val;
	return true;
}
@@ -150,13 +217,13 @@ static bool __maybe_unused
xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value)
{
	return xdrgen_encode_hyper(xdr, value);
};
}

static bool __maybe_unused
xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value)
{
	return xdrgen_encode_unsigned_int(xdr, value);
};
}

static bool __maybe_unused
xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
@@ -167,7 +234,7 @@ xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value)
		if (!xdrgen_encode_uint32_t(xdr, value.element[i]))
			return false;
	return true;
};
}

static bool __maybe_unused
xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
@@ -177,13 +244,13 @@ xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value)
	if (!xdrgen_encode_uint32_t(xdr, value->nseconds))
		return false;
	return true;
};
}

static bool __maybe_unused
xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value)
{
	return xdrgen_encode_bool(xdr, value);
};
}

static bool __maybe_unused
xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value)
@@ -199,7 +266,7 @@ xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_argument
	if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode))
		return false;
	return true;
};
}

static bool __maybe_unused
xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 value)
@@ -235,19 +302,19 @@ bool
xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value)
{
	return xdrgen_encode_open_arguments4(xdr, value);
};
}

bool
xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value)
{
	return xdrgen_encode_nfstime4(xdr, value);
};
}

bool
xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value)
{
	return xdrgen_encode_nfstime4(xdr, value);
};
}

static bool __maybe_unused
xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 value)
+1 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
/* XDR specification modification time: Thu Dec 25 13:44:43 2025 */

#ifndef _LINUX_XDRGEN_NFS4_1_DECL_H
#define _LINUX_XDRGEN_NFS4_1_DECL_H
+7 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */
/* XDR specification modification time: Mon Oct 14 09:10:13 2024 */
/* XDR specification modification time: Thu Dec 25 13:44:43 2025 */

#ifndef _LINUX_XDRGEN_NFS4_1_DEF_H
#define _LINUX_XDRGEN_NFS4_1_DEF_H
@@ -40,6 +40,7 @@ enum open_args_share_access4 {
	OPEN_ARGS_SHARE_ACCESS_WRITE = 2,
	OPEN_ARGS_SHARE_ACCESS_BOTH = 3,
};

typedef enum open_args_share_access4 open_args_share_access4;

enum open_args_share_deny4 {
@@ -48,6 +49,7 @@ enum open_args_share_deny4 {
	OPEN_ARGS_SHARE_DENY_WRITE = 2,
	OPEN_ARGS_SHARE_DENY_BOTH = 3,
};

typedef enum open_args_share_deny4 open_args_share_deny4;

enum open_args_share_access_want4 {
@@ -59,6 +61,7 @@ enum open_args_share_access_want4 {
	OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20,
	OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21,
};

typedef enum open_args_share_access_want4 open_args_share_access_want4;

enum open_args_open_claim4 {
@@ -70,6 +73,7 @@ enum open_args_open_claim4 {
	OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5,
	OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6,
};

typedef enum open_args_open_claim4 open_args_open_claim4;

enum open_args_createmode4 {
@@ -78,6 +82,7 @@ enum open_args_createmode4 {
	OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2,
	OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3,
};

typedef enum open_args_createmode4 open_args_createmode4;

typedef struct open_arguments4 fattr4_open_arguments;
@@ -124,6 +129,7 @@ enum open_delegation_type4 {
	OPEN_DELEGATE_READ_ATTRS_DELEG = 4,
	OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5,
};

typedef enum open_delegation_type4 open_delegation_type4;

#define NFS4_int64_t_sz                 \
+8 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@

from generators import SourceGenerator, create_jinja2_environment
from xdr_ast import _XdrEnum, public_apis, big_endian, get_header_name
from xdr_parse import get_xdr_enum_validation


class XdrEnumGenerator(SourceGenerator):
@@ -42,7 +43,13 @@ class XdrEnumGenerator(SourceGenerator):
            template = self.environment.get_template("decoder/enum_be.j2")
        else:
            template = self.environment.get_template("decoder/enum.j2")
        print(template.render(name=node.name))
        print(
            template.render(
                name=node.name,
                enumerators=node.enumerators,
                validate=get_xdr_enum_validation(),
            )
        )

    def emit_encoder(self, node: _XdrEnum) -> None:
        """Emit one encoder function for an XDR enum type"""
+2 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ from xdr_ast import transform_parse_tree, _RpcProgram, Specification
from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer
from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion

from xdr_parse import xdr_parser, set_xdr_annotate
from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation
from xdr_parse import make_error_handler, XdrParseError
from xdr_parse import handle_transform_error

@@ -98,6 +98,7 @@ def subcmd(args: Namespace) -> int:
    """Generate encoder and decoder functions"""

    set_xdr_annotate(args.annotate)
    set_xdr_enum_validation(not args.no_enum_validation)
    parser = xdr_parser()
    with open(args.filename, encoding="utf-8") as f:
        source = f.read()
Loading