[PATCH 4/5] xdrgen: Enforce RFC 5531 name and number scoping for RPC programs

Chuck Lever <[email protected]>
Newsgroups gmane.linux.nfs
Message-ID <[email protected]>
The duplicate-identifier check enforces the RFC 4506 name space
for XDR type and constant identifiers but ignores what an RPC
program definition adds. RFC 5531 Section 12.3 completes the
model: a program identifier shares the specification-wide name
space with constant and type identifiers, a version name and
number are unique within their program, and a procedure name and
number are unique within their version.

xdrgen currently accepts a specification that breaks any of these
rules, and the symptom depends on which rule. A duplicate procedure
name reaches the generated header as a redeclared enumerator,
which the C compiler rejects. A duplicate procedure number is
more dangerous because it is silent: the two procedures emit
enumerators of equal value -- valid C that compiles cleanly --
leaving a dispatch collision to surface only at run time. A
duplicate program name shares the specification-wide name space
with constants and types and is caught alongside them.

Extend the check to enforce RFC 5531 scoping in full.

Signed-off-by: Chuck Lever <[email protected]>
---
 tools/net/sunrpc/xdrgen/xdr_ast.py | 49 ++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/tools/net/sunrpc/xdrgen/xdr_ast.py b/tools/net/sunrpc/xdrgen/xdr_ast.py
index cf68ff5dfe17..ec48506b239a 100644
--- a/tools/net/sunrpc/xdrgen/xdr_ast.py
+++ b/tools/net/sunrpc/xdrgen/xdr_ast.py
@@ -862,6 +862,50 @@ def _introduced_names(value):
         yield value.declaration.name, value.declaration
     elif isinstance(value, _XdrConstant):
         yield value.name, value
+    elif isinstance(value, _RpcProgram):
+        yield value.name, value
+
+
+def _check_rpc_scope_names(program: "_RpcProgram") -> None:
+    """Enforce RFC 5531 Section 12.3 scoping within an RPC program.
+
+    A version name and number are unique within the program and a
+    procedure name and number are unique within its version.
+    """
+    version_names = set()
+    version_numbers = set()
+    for version in program.versions:
+        if version.name in version_names:
+            raise XdrSemanticError(
+                f"duplicate version name '{version.name}'"
+                f" in program '{program.name}'",
+                version,
+            )
+        version_names.add(version.name)
+        if version.number in version_numbers:
+            raise XdrSemanticError(
+                f"duplicate version number {version.number}"
+                f" in program '{program.name}'",
+                version,
+            )
+        version_numbers.add(version.number)
+        procedure_names = set()
+        procedure_numbers = set()
+        for procedure in version.procedures:
+            if procedure.name in procedure_names:
+                raise XdrSemanticError(
+                    f"duplicate procedure name '{procedure.name}'"
+                    f" in version '{version.name}'",
+                    procedure,
+                )
+            procedure_names.add(procedure.name)
+            if procedure.number in procedure_numbers:
+                raise XdrSemanticError(
+                    f"duplicate procedure number {procedure.number}"
+                    f" in version '{version.name}'",
+                    procedure,
+                )
+            procedure_numbers.add(procedure.number)
 
 
 def check_duplicate_definitions(root: "Specification") -> None:
@@ -869,6 +913,9 @@ def check_duplicate_definitions(root: "Specification") -> None:
 
     RFC 4506 Section 6.4 places constant and type identifiers in a
     single name space that must be unique within a specification.
+    RFC 5531 Section 12.3 adds RPC program names to that name space
+    and scopes version names and numbers to their program and
+    procedure names and numbers to their version.
     """
     seen = {}
     for definition in root.definitions:
@@ -882,6 +929,8 @@ def check_duplicate_definitions(root: "Specification") -> None:
                     where,
                 )
             seen[name] = where
+        if isinstance(definition.value, _RpcProgram):
+            _check_rpc_scope_names(definition.value)
 
 
 def transform_parse_tree(parse_tree):
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.