Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 1c873a2fd110 ("xdrgen: Don't generate unnecessary semicolon")
and commit 38c4df91242b ("xdrgen: Address some checkpatch whitespace
complaints").

Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

+156 -28
+86 -19
fs/nfsd/nfs4xdr_gen.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 // Generated by xdrgen. Manual edits will be lost. 3 3 // XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x 4 - // XDR specification modification time: Mon Oct 14 09:10:13 2024 4 + // XDR specification modification time: Thu Dec 25 13:44:43 2025 5 5 6 6 #include <linux/sunrpc/svc.h> 7 7 ··· 11 11 xdrgen_decode_int64_t(struct xdr_stream *xdr, int64_t *ptr) 12 12 { 13 13 return xdrgen_decode_hyper(xdr, ptr); 14 - }; 14 + } 15 15 16 16 static bool __maybe_unused 17 17 xdrgen_decode_uint32_t(struct xdr_stream *xdr, uint32_t *ptr) 18 18 { 19 19 return xdrgen_decode_unsigned_int(xdr, ptr); 20 - }; 20 + } 21 21 22 22 static bool __maybe_unused 23 23 xdrgen_decode_bitmap4(struct xdr_stream *xdr, bitmap4 *ptr) ··· 28 28 if (!xdrgen_decode_uint32_t(xdr, &ptr->element[i])) 29 29 return false; 30 30 return true; 31 - }; 31 + } 32 32 33 33 static bool __maybe_unused 34 34 xdrgen_decode_nfstime4(struct xdr_stream *xdr, struct nfstime4 *ptr) ··· 38 38 if (!xdrgen_decode_uint32_t(xdr, &ptr->nseconds)) 39 39 return false; 40 40 return true; 41 - }; 41 + } 42 42 43 43 static bool __maybe_unused 44 44 xdrgen_decode_fattr4_offline(struct xdr_stream *xdr, fattr4_offline *ptr) 45 45 { 46 46 return xdrgen_decode_bool(xdr, ptr); 47 - }; 47 + } 48 48 49 49 static bool __maybe_unused 50 50 xdrgen_decode_open_arguments4(struct xdr_stream *xdr, struct open_arguments4 *ptr) ··· 60 60 if (!xdrgen_decode_bitmap4(xdr, &ptr->oa_create_mode)) 61 61 return false; 62 62 return true; 63 - }; 63 + } 64 64 65 65 static bool __maybe_unused 66 66 xdrgen_decode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 *ptr) ··· 69 69 70 70 if (xdr_stream_decode_u32(xdr, &val) < 0) 71 71 return false; 72 + /* Compiler may optimize to a range check for dense enums */ 73 + switch (val) { 74 + case OPEN_ARGS_SHARE_ACCESS_READ: 75 + case OPEN_ARGS_SHARE_ACCESS_WRITE: 76 + case OPEN_ARGS_SHARE_ACCESS_BOTH: 77 + break; 78 + default: 79 + return false; 80 + } 72 81 *ptr = val; 73 82 return true; 74 83 } ··· 89 80 90 81 if (xdr_stream_decode_u32(xdr, &val) < 0) 91 82 return false; 83 + /* Compiler may optimize to a range check for dense enums */ 84 + switch (val) { 85 + case OPEN_ARGS_SHARE_DENY_NONE: 86 + case OPEN_ARGS_SHARE_DENY_READ: 87 + case OPEN_ARGS_SHARE_DENY_WRITE: 88 + case OPEN_ARGS_SHARE_DENY_BOTH: 89 + break; 90 + default: 91 + return false; 92 + } 92 93 *ptr = val; 93 94 return true; 94 95 } ··· 110 91 111 92 if (xdr_stream_decode_u32(xdr, &val) < 0) 112 93 return false; 94 + /* Compiler may optimize to a range check for dense enums */ 95 + switch (val) { 96 + case OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG: 97 + case OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG: 98 + case OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL: 99 + case OPEN_ARGS_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL: 100 + case OPEN_ARGS_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED: 101 + case OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS: 102 + case OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION: 103 + break; 104 + default: 105 + return false; 106 + } 113 107 *ptr = val; 114 108 return true; 115 109 } ··· 134 102 135 103 if (xdr_stream_decode_u32(xdr, &val) < 0) 136 104 return false; 105 + /* Compiler may optimize to a range check for dense enums */ 106 + switch (val) { 107 + case OPEN_ARGS_OPEN_CLAIM_NULL: 108 + case OPEN_ARGS_OPEN_CLAIM_PREVIOUS: 109 + case OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR: 110 + case OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV: 111 + case OPEN_ARGS_OPEN_CLAIM_FH: 112 + case OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH: 113 + case OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH: 114 + break; 115 + default: 116 + return false; 117 + } 137 118 *ptr = val; 138 119 return true; 139 120 } ··· 158 113 159 114 if (xdr_stream_decode_u32(xdr, &val) < 0) 160 115 return false; 116 + /* Compiler may optimize to a range check for dense enums */ 117 + switch (val) { 118 + case OPEN_ARGS_CREATEMODE_UNCHECKED4: 119 + case OPEN_ARGS_CREATE_MODE_GUARDED: 120 + case OPEN_ARGS_CREATEMODE_EXCLUSIVE4: 121 + case OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1: 122 + break; 123 + default: 124 + return false; 125 + } 161 126 *ptr = val; 162 127 return true; 163 128 } ··· 176 121 xdrgen_decode_fattr4_open_arguments(struct xdr_stream *xdr, fattr4_open_arguments *ptr) 177 122 { 178 123 return xdrgen_decode_open_arguments4(xdr, ptr); 179 - }; 124 + } 180 125 181 126 bool 182 127 xdrgen_decode_fattr4_time_deleg_access(struct xdr_stream *xdr, fattr4_time_deleg_access *ptr) 183 128 { 184 129 return xdrgen_decode_nfstime4(xdr, ptr); 185 - }; 130 + } 186 131 187 132 bool 188 133 xdrgen_decode_fattr4_time_deleg_modify(struct xdr_stream *xdr, fattr4_time_deleg_modify *ptr) 189 134 { 190 135 return xdrgen_decode_nfstime4(xdr, ptr); 191 - }; 136 + } 192 137 193 138 static bool __maybe_unused 194 139 xdrgen_decode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 *ptr) ··· 197 142 198 143 if (xdr_stream_decode_u32(xdr, &val) < 0) 199 144 return false; 145 + /* Compiler may optimize to a range check for dense enums */ 146 + switch (val) { 147 + case OPEN_DELEGATE_NONE: 148 + case OPEN_DELEGATE_READ: 149 + case OPEN_DELEGATE_WRITE: 150 + case OPEN_DELEGATE_NONE_EXT: 151 + case OPEN_DELEGATE_READ_ATTRS_DELEG: 152 + case OPEN_DELEGATE_WRITE_ATTRS_DELEG: 153 + break; 154 + default: 155 + return false; 156 + } 200 157 *ptr = val; 201 158 return true; 202 159 } ··· 217 150 xdrgen_encode_int64_t(struct xdr_stream *xdr, const int64_t value) 218 151 { 219 152 return xdrgen_encode_hyper(xdr, value); 220 - }; 153 + } 221 154 222 155 static bool __maybe_unused 223 156 xdrgen_encode_uint32_t(struct xdr_stream *xdr, const uint32_t value) 224 157 { 225 158 return xdrgen_encode_unsigned_int(xdr, value); 226 - }; 159 + } 227 160 228 161 static bool __maybe_unused 229 162 xdrgen_encode_bitmap4(struct xdr_stream *xdr, const bitmap4 value) ··· 234 167 if (!xdrgen_encode_uint32_t(xdr, value.element[i])) 235 168 return false; 236 169 return true; 237 - }; 170 + } 238 171 239 172 static bool __maybe_unused 240 173 xdrgen_encode_nfstime4(struct xdr_stream *xdr, const struct nfstime4 *value) ··· 244 177 if (!xdrgen_encode_uint32_t(xdr, value->nseconds)) 245 178 return false; 246 179 return true; 247 - }; 180 + } 248 181 249 182 static bool __maybe_unused 250 183 xdrgen_encode_fattr4_offline(struct xdr_stream *xdr, const fattr4_offline value) 251 184 { 252 185 return xdrgen_encode_bool(xdr, value); 253 - }; 186 + } 254 187 255 188 static bool __maybe_unused 256 189 xdrgen_encode_open_arguments4(struct xdr_stream *xdr, const struct open_arguments4 *value) ··· 266 199 if (!xdrgen_encode_bitmap4(xdr, value->oa_create_mode)) 267 200 return false; 268 201 return true; 269 - }; 202 + } 270 203 271 204 static bool __maybe_unused 272 205 xdrgen_encode_open_args_share_access4(struct xdr_stream *xdr, open_args_share_access4 value) ··· 302 235 xdrgen_encode_fattr4_open_arguments(struct xdr_stream *xdr, const fattr4_open_arguments *value) 303 236 { 304 237 return xdrgen_encode_open_arguments4(xdr, value); 305 - }; 238 + } 306 239 307 240 bool 308 241 xdrgen_encode_fattr4_time_deleg_access(struct xdr_stream *xdr, const fattr4_time_deleg_access *value) 309 242 { 310 243 return xdrgen_encode_nfstime4(xdr, value); 311 - }; 244 + } 312 245 313 246 bool 314 247 xdrgen_encode_fattr4_time_deleg_modify(struct xdr_stream *xdr, const fattr4_time_deleg_modify *value) 315 248 { 316 249 return xdrgen_encode_nfstime4(xdr, value); 317 - }; 250 + } 318 251 319 252 static bool __maybe_unused 320 253 xdrgen_encode_open_delegation_type4(struct xdr_stream *xdr, open_delegation_type4 value)
+1 -1
fs/nfsd/nfs4xdr_gen.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Generated by xdrgen. Manual edits will be lost. */ 3 3 /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */ 4 - /* XDR specification modification time: Mon Oct 14 09:10:13 2024 */ 4 + /* XDR specification modification time: Thu Dec 25 13:44:43 2025 */ 5 5 6 6 #ifndef _LINUX_XDRGEN_NFS4_1_DECL_H 7 7 #define _LINUX_XDRGEN_NFS4_1_DECL_H
+7 -1
include/linux/sunrpc/xdrgen/nfs4_1.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Generated by xdrgen. Manual edits will be lost. */ 3 3 /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs4_1.x */ 4 - /* XDR specification modification time: Mon Oct 14 09:10:13 2024 */ 4 + /* XDR specification modification time: Thu Dec 25 13:44:43 2025 */ 5 5 6 6 #ifndef _LINUX_XDRGEN_NFS4_1_DEF_H 7 7 #define _LINUX_XDRGEN_NFS4_1_DEF_H ··· 40 40 OPEN_ARGS_SHARE_ACCESS_WRITE = 2, 41 41 OPEN_ARGS_SHARE_ACCESS_BOTH = 3, 42 42 }; 43 + 43 44 typedef enum open_args_share_access4 open_args_share_access4; 44 45 45 46 enum open_args_share_deny4 { ··· 49 48 OPEN_ARGS_SHARE_DENY_WRITE = 2, 50 49 OPEN_ARGS_SHARE_DENY_BOTH = 3, 51 50 }; 51 + 52 52 typedef enum open_args_share_deny4 open_args_share_deny4; 53 53 54 54 enum open_args_share_access_want4 { ··· 61 59 OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS = 20, 62 60 OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION = 21, 63 61 }; 62 + 64 63 typedef enum open_args_share_access_want4 open_args_share_access_want4; 65 64 66 65 enum open_args_open_claim4 { ··· 73 70 OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH = 5, 74 71 OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH = 6, 75 72 }; 73 + 76 74 typedef enum open_args_open_claim4 open_args_open_claim4; 77 75 78 76 enum open_args_createmode4 { ··· 82 78 OPEN_ARGS_CREATEMODE_EXCLUSIVE4 = 2, 83 79 OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1 = 3, 84 80 }; 81 + 85 82 typedef enum open_args_createmode4 open_args_createmode4; 86 83 87 84 typedef struct open_arguments4 fattr4_open_arguments; ··· 129 124 OPEN_DELEGATE_READ_ATTRS_DELEG = 4, 130 125 OPEN_DELEGATE_WRITE_ATTRS_DELEG = 5, 131 126 }; 127 + 132 128 typedef enum open_delegation_type4 open_delegation_type4; 133 129 134 130 #define NFS4_int64_t_sz \
+8 -1
tools/net/sunrpc/xdrgen/generators/enum.py
··· 5 5 6 6 from generators import SourceGenerator, create_jinja2_environment 7 7 from xdr_ast import _XdrEnum, public_apis, big_endian, get_header_name 8 + from xdr_parse import get_xdr_enum_validation 8 9 9 10 10 11 class XdrEnumGenerator(SourceGenerator): ··· 43 42 template = self.environment.get_template("decoder/enum_be.j2") 44 43 else: 45 44 template = self.environment.get_template("decoder/enum.j2") 46 - print(template.render(name=node.name)) 45 + print( 46 + template.render( 47 + name=node.name, 48 + enumerators=node.enumerators, 49 + validate=get_xdr_enum_validation(), 50 + ) 51 + ) 47 52 48 53 def emit_encoder(self, node: _XdrEnum) -> None: 49 54 """Emit one encoder function for an XDR enum type"""
+2 -1
tools/net/sunrpc/xdrgen/subcmds/source.py
··· 22 22 from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer 23 23 from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion 24 24 25 - from xdr_parse import xdr_parser, set_xdr_annotate 25 + from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation 26 26 from xdr_parse import make_error_handler, XdrParseError 27 27 from xdr_parse import handle_transform_error 28 28 ··· 98 98 """Generate encoder and decoder functions""" 99 99 100 100 set_xdr_annotate(args.annotate) 101 + set_xdr_enum_validation(not args.no_enum_validation) 101 102 parser = xdr_parser() 102 103 with open(args.filename, encoding="utf-8") as f: 103 104 source = f.read()
+11
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
··· 14 14 15 15 if (xdr_stream_decode_u32(xdr, &val) < 0) 16 16 return false; 17 + {% if validate and enumerators %} 18 + /* Compiler may optimize to a range check for dense enums */ 19 + switch (val) { 20 + {% for e in enumerators %} 21 + case {{ e.name }}: 22 + {% endfor %} 23 + break; 24 + default: 25 + return false; 26 + } 27 + {% endif %} 17 28 *ptr = val; 18 29 return true; 19 30 }
+20
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2
··· 10 10 {% endif %} 11 11 xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) 12 12 { 13 + {% if validate and enumerators %} 14 + __be32 raw; 15 + u32 val; 16 + 17 + if (xdr_stream_decode_be32(xdr, &raw) < 0) 18 + return false; 19 + val = be32_to_cpu(raw); 20 + /* Compiler may optimize to a range check for dense enums */ 21 + switch (val) { 22 + {% for e in enumerators %} 23 + case {{ e.name }}: 24 + {% endfor %} 25 + break; 26 + default: 27 + return false; 28 + } 29 + *ptr = raw; 30 + return true; 31 + {% else %} 13 32 return xdr_stream_decode_be32(xdr, ptr) == 0; 33 + {% endif %} 14 34 }
+1 -5
tools/net/sunrpc/xdrgen/xdr_ast.py
··· 330 330 """An XDR enum definition""" 331 331 332 332 name: str 333 - minimum: int 334 - maximum: int 335 333 enumerators: List[_XdrEnumerator] 336 334 337 335 def max_width(self) -> int: ··· 570 572 value = children[1].value 571 573 return _XdrConstant(name, value) 572 574 573 - # cel: Python can compute a min() and max() for the enumerator values 574 - # so that the generated code can perform proper range checking. 575 575 def enum(self, children): 576 576 """Instantiate one _XdrEnum object""" 577 577 enum_name = children[0].symbol ··· 583 587 enumerators.append(_XdrEnumerator(name, value)) 584 588 i = i + 2 585 589 586 - return _XdrEnum(enum_name, 0, 0, enumerators) 590 + return _XdrEnum(enum_name, enumerators) 587 591 588 592 def fixed_length_opaque(self, children): 589 593 """Instantiate one _XdrFixedLengthOpaque declaration object"""
+14
tools/net/sunrpc/xdrgen/xdr_parse.py
··· 13 13 # Set to True to emit annotation comments in generated source 14 14 annotate = False 15 15 16 + # Set to True to emit enum value validation in decoders 17 + enum_validation = True 18 + 16 19 # Map internal Lark token names to human-readable names 17 20 TOKEN_NAMES = { 18 21 "__ANON_0": "identifier", ··· 50 47 def get_xdr_annotate() -> bool: 51 48 """Return True if --annotate was specified on the command line""" 52 49 return annotate 50 + 51 + 52 + def set_xdr_enum_validation(set_it: bool) -> None: 53 + """Set 'enum_validation' based on command line options""" 54 + global enum_validation 55 + enum_validation = set_it 56 + 57 + 58 + def get_xdr_enum_validation() -> bool: 59 + """Return True when enum validation is enabled for decoder generation""" 60 + return enum_validation 53 61 54 62 55 63 def make_error_handler(source: str, filename: str) -> Callable[[UnexpectedInput], bool]:
+6
tools/net/sunrpc/xdrgen/xdrgen
··· 123 123 help="Generate code for client or server side", 124 124 type=str, 125 125 ) 126 + source_parser.add_argument( 127 + "--no-enum-validation", 128 + action="store_true", 129 + default=False, 130 + help="Disable enum value validation in decoders", 131 + ) 126 132 source_parser.add_argument("filename", help="File containing an XDR specification") 127 133 source_parser.set_defaults(func=source.subcmd) 128 134