[DTrace-devel] [PATCH] parser: validate scoped external names

Alan Maguire alan.maguire at oracle.com
Fri Sep 11 14:32:12 UTC 2026


On 28/08/2026 19:48, Kris Van Hees via DTrace-devel wrote:
> Centralize parsing of D scoping markers used by external type and symbol
> references.  The helper now returns the resolved scope and identifier
> pieces, supports kernel and userspace markers for both type and symbol
> lookup, and rejects empty operands, runs longer than two backticks, and
> repeated scoping markers as D_SYNTAX instead of falling through to lookup
> failures.
> 
> Recognize dotted scoped identifiers as a single lexer token so module
> names like foo.bar resolve as one external symbol reference.
> 
> Add lexer and type regression tests for malformed scoped identifiers,
> dotted scopes, empty scope operands, scope-kind mismatches, and scoping
> operators in declarations.  Update the sizeof unknown-symbol test to use
> a non-empty symbol name.
> 
> Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>

One AI-assisted issue found here I think; D allows us to scope type references IIRC; i.e.

struct vmlinux`task_struct *task;

or sizeof(struct vmlinux`task_struct)

However the backtick support here also allows a backtick in local type declarations
which doesn't make sense:

struct vmlinux`task_struct {
      int replacement;
};

We can likely distinguish type declaration (no ` allowed) from scoped variable
declaration in dt_decl_sou()/dt_decl_enum() but I think we need to do so.
 
> ---
>  libdtrace/dt_decl.c                           |   6 +-
>  libdtrace/dt_lex.l                            |   5 +
>  libdtrace/dt_parser.c                         | 140 ++++++++++++------
>  libdtrace/dt_parser.h                         |   1 +
>  .../err.D_IDENT_UNDEF.scope-dot-kernel.d      |  19 +++
>  .../err.D_IDENT_UNDEF.scope-dot-kernel.r      |   2 +
>  .../lexer/err.D_IDENT_UNDEF.scope-dot.d       |  19 +++
>  .../lexer/err.D_IDENT_UNDEF.scope-dot.r       |   2 +
>  .../err.D_SYM_BADREF.scope-kernel-as-user.d   |  18 +++
>  .../err.D_SYM_BADREF.scope-kernel-as-user.r   |   2 +
>  .../err.D_SYM_BADREF.scope-user-as-kernel.d   |  20 +++
>  .../err.D_SYM_BADREF.scope-user-as-kernel.r   |   2 +
>  .../err.D_SYNTAX.scope-empty-bare-user.d      |  19 +++
>  .../err.D_SYNTAX.scope-empty-bare-user.r      |   2 +
>  .../lexer/err.D_SYNTAX.scope-empty-bare.d     |  19 +++
>  .../lexer/err.D_SYNTAX.scope-empty-bare.r     |   2 +
>  .../lexer/err.D_SYNTAX.scope-empty-dot-user.d |  19 +++
>  .../lexer/err.D_SYNTAX.scope-empty-dot-user.r |   2 +
>  .../lexer/err.D_SYNTAX.scope-empty-user.d     |  19 +++
>  .../lexer/err.D_SYNTAX.scope-empty-user.r     |   2 +
>  .../unittest/lexer/err.D_SYNTAX.scope-empty.d |  18 +++
>  .../unittest/lexer/err.D_SYNTAX.scope-empty.r |   2 +
>  .../lexer/err.D_SYNTAX.scope-multiple.d       |  19 +++
>  .../lexer/err.D_SYNTAX.scope-multiple.r       |   2 +
>  .../lexer/err.D_SYNTAX.scope-triple.d         |  19 +++
>  .../lexer/err.D_SYNTAX.scope-triple.r         |   2 +
>  test/unittest/lexer/tst.scope-user-explicit.d |  22 +++
>  .../sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d  |   2 +-
>  .../sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r  |   2 +-
>  .../err.D_DECL_SCOPE.scopeop-enumerator.d     |  16 ++
>  .../err.D_DECL_SCOPE.scopeop-enumerator.r     |   2 +
>  .../types/err.D_DECL_SCOPE.scopeop-ident.d    |  19 +++
>  .../types/err.D_DECL_SCOPE.scopeop-ident.r    |   2 +
>  .../types/err.D_DECL_SCOPE.scopeop-member.d   |  16 ++
>  .../types/err.D_DECL_SCOPE.scopeop-member.r   |   2 +
>  35 files changed, 414 insertions(+), 51 deletions(-)
>  create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d
>  create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r
>  create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d
>  create mode 100644 test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r
>  create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d
>  create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r
>  create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d
>  create mode 100644 test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-empty.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-multiple.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-multiple.r
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-triple.d
>  create mode 100644 test/unittest/lexer/err.D_SYNTAX.scope-triple.r
>  create mode 100644 test/unittest/lexer/tst.scope-user-explicit.d
>  create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d
>  create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r
>  create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d
>  create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r
>  create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d
>  create mode 100644 test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r
> 
> diff --git a/libdtrace/dt_decl.c b/libdtrace/dt_decl.c
> index 1f4936dd..68999422 100644
> --- a/libdtrace/dt_decl.c
> +++ b/libdtrace/dt_decl.c
> @@ -233,10 +233,8 @@ dt_decl_spec(ushort_t kind, char *name)
>  	ddp->dd_kind = kind;
>  	ddp->dd_name = name;
>  
> -	if (name != NULL && strchr(name, '`') != NULL) {
> -		xyerror(D_DECL_SCOPE, "D scoping operator may not be used "
> -		    "in a type name\n");
> -	}
> +	if (name != NULL && dt_scope_parse(name, NULL, NULL) < 0)
> +		xyerror(D_SYNTAX, "syntax error near \"%s\"\n", name);
>  
>  	return dt_decl_check(ddp);
>  }
> diff --git a/libdtrace/dt_lex.l b/libdtrace/dt_lex.l
> index fd70aa0a..bf133676 100644
> --- a/libdtrace/dt_lex.l
> +++ b/libdtrace/dt_lex.l
> @@ -50,6 +50,7 @@ static size_t dt_input(char *buf, size_t max_size);
>  RGX_AGG		"@"[a-zA-Z_][0-9a-zA-Z_]*
>  RGX_PSPEC	[-$:a-zA-Z_.?*\\\[\]!][-$:0-9a-zA-Z_.`?*\\\[\]!]*
>  RGX_IDENT	([a-zA-Z_`][0-9a-zA-Z_`]*)|([0-9][0-9a-zA-Z_]*`[0-9a-zA-Z_`]*)
> +RGX_SCOPE_IDENT	[0-9a-zA-Z_][0-9a-zA-Z_]*(\.[0-9a-zA-Z_]+)+``?[0-9a-zA-Z_]+
>  RGX_INT		([0-9]+|0[xX][0-9A-Fa-f]+)[uU]?[lL]?[lL]?
>  RGX_FP		([0-9]+("."?)[0-9]*|"."[0-9]+)((e|E)("+"|-)?[0-9]+)?[fFlL]?
>  RGX_WS		[\f\n\r\t\v ]
> @@ -299,6 +300,7 @@ if (yypcb->pcb_token != 0) {
>  			return DT_TOK_INT;
>  		}
>  
> +<S0>{RGX_SCOPE_IDENT} |
>  <S0,SIDENT>{RGX_IDENT}	{
>  			return id_or_type(yytext);
>  		}
> @@ -728,6 +730,9 @@ id_or_type(const char *s)
>  	int c0, c1;
>  	dt_ident_t *idp;
>  
> +	if (dt_scope_parse(s, NULL, NULL) < 0)
> +		xyerror(D_SYNTAX, "syntax error near \"%s\"\n", s);
> +
>  	if ((s = yylval.l_str = strdup(s)) == NULL)
>  		longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
>  
> diff --git a/libdtrace/dt_parser.c b/libdtrace/dt_parser.c
> index e71c0985..1fd0f5b4 100644
> --- a/libdtrace/dt_parser.c
> +++ b/libdtrace/dt_parser.c
> @@ -168,45 +168,96 @@ opstr(int op)
>  	}
>  }
>  
> +/*
> + * Parse the D scoping mark used in external type and symbol names.  A scoped
> + * name can contain one mark only: ` for kernel scope, or `` for user scope.
> + * Longer runs and repeated marks are malformed.  Return the scope kind as 0
> + * for no scope, 1 for kernel scope, or 2 for user scope.
> + */
>  int
> -dt_type_lookup(const char *s, dtrace_typeinfo_t *tip)
> +dt_scope_parse(const char *s, const char **scopep, const char **identp)
>  {
> -	static const char delimiters[] = " \t\n\r\v\f*`";
> -	dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> -	const char *p, *q, *end, *obj;
> +	static const char delimiters[] = " \t\n\r\v\f*";
> +	const char *mark = strchr(s, '`');
> +	const char *scope;
> +	size_t marklen = 1;
> +
> +	if (mark == NULL) {
> +		if (scopep != NULL)
> +			*scopep = DTRACE_OBJ_EXEC;
> +		if (identp != NULL)
> +			*identp = s;
> +		return 0;
> +	}
>  
> -	for (p = s, end = s + strlen(s); *p != '\0'; p = q) {
> -		while (isspace(*p))
> -			p++;	/* skip leading whitespace prior to token */
> +	if (mark[1] == '`') {
> +		marklen = 2;
> +		if (mark[2] == '`')
> +			return -1;
> +	}
>  
> -		if (p == end || (q = strpbrk(p + 1, delimiters)) == NULL)
> -			break;	/* empty string or single token remaining */
> +	if (mark[marklen] == '\0')
> +		return -1;
>  
> -		if (*q == '`') {
> -			char *object = alloca((size_t)(q - p) + 1);
> -			char *type = alloca((size_t)(end - s) + 1);
> +	if (strchr(mark + marklen, '`') != NULL)
> +		return -1;
>  
> -			/*
> -			 * Copy from the start of the token (p) to the location
> -			 * backquote (q) to extract the nul-terminated object.
> -			 */
> -			memcpy(object, p, (size_t)(q - p));
> -			object[(size_t)(q - p)] = '\0';
> +	for (scope = mark;
> +	    scope > s && strchr(delimiters, scope[-1]) == NULL; )
> +		scope--;
>  
> -			/*
> -			 * Copy the original string up to the start of this
> -			 * token (p) into type, and then concatenate everything
> -			 * after q.  This is the type name without the object.
> -			 */
> -			memcpy(type, s, (size_t)(p - s));
> -			memcpy(type + (size_t)(p - s), q + 1,
> -			    strlen(q + 1) + 1);
> +	if (scope == mark)
> +		scope = marklen == 2 ? DTRACE_OBJ_UMODS : DTRACE_OBJ_KMODS;
> +
> +	if (scopep != NULL)
> +		*scopep = scope;
> +	if (identp != NULL)
> +		*identp = mark + marklen;
> +
> +	return marklen;
> +}
> +
> +int
> +dt_type_lookup(const char *s, dtrace_typeinfo_t *tip)
> +{
> +	dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> +	const char *scope, *ident, *start, *obj;
> +	int scopekind;
> +
> +	scopekind = dt_scope_parse(s, &scope, &ident);
> +	if (scopekind < 0)
> +		return dt_set_errno(dtp, EDT_BADSCOPE);
>  
> -			if (strchr(q + 1, '`') != NULL)
> -				return dt_set_errno(dtp, EDT_BADSCOPE);
> +	if (scopekind > 0) {
> +		char *type;
> +
> +		/*
> +		 * If there is no explicit object before the mark, search all
> +		 * kernel or user objects depending on the mark width.
> +		 */
> +		if (scope == DTRACE_OBJ_KMODS || scope == DTRACE_OBJ_UMODS) {
> +			start = ident - scopekind;
> +			obj = scope;
> +		} else {
> +			size_t objlen = (size_t)(ident - scope) - scopekind;
> +			char *object = alloca(objlen + 1);
>  
> -			return dtrace_lookup_by_type(dtp, object, type, tip);
> +			memcpy(object, scope, objlen);
> +			object[objlen] = '\0';
> +			start = scope;
> +			obj = object;
>  		}
> +
> +		/*
> +		 * Copy the original string up to the start of this token
> +		 * into type, and then concatenate everything after the scoping
> +		 * mark.  This is the type name without the object and mark.
> +		 */
> +		type = alloca((size_t)(start - s) + strlen(ident) + 1);
> +		memcpy(type, s, (size_t)(start - s));
> +		memcpy(type + (size_t)(start - s), ident, strlen(ident) + 1);
> +
> +		return dtrace_lookup_by_type(dtp, obj, type, tip);
>  	}
>  
>  	if (yypcb->pcb_idepth != 0)
> @@ -2767,9 +2818,10 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
>  	dtrace_syminfo_t dts;
>  	GElf_Sym sym;
>  
> -	const char *scope, *mark;
> +	const char *scope, *ident, *markstr;
>  	uchar_t dnkind;
>  	char *name;
> +	int scopekind;
>  
>  	/*
>  	 * Look for scoping marks in the identifier.  If one is found, set our
> @@ -2779,18 +2831,16 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
>  	 * Otherwise we set scope to DTRACE_OBJ_EXEC, indicating that normal
>  	 * scope is desired and we should search the specified idhash.
>  	 */
> -	if ((name = strrchr(dnp->dn_string, '`')) != NULL) {
> -		if (name > dnp->dn_string && name[-1] == '`') {
> -			uref++;
> -			name[-1] = '\0';
> -		}
> -
> -		if (name == dnp->dn_string + uref)
> -			scope = uref ? DTRACE_OBJ_UMODS : DTRACE_OBJ_KMODS;
> -		else
> -			scope = dnp->dn_string;
> +	scopekind = dt_scope_parse(dnp->dn_string, &scope, &ident);
> +	if (scopekind < 0) {
> +		xyerror(D_SYNTAX, "syntax error near \"%s\"\n",
> +		    dnp->dn_string);
> +	}
>  
> -		*name++ = '\0'; /* leave name pointing after scoping mark */
> +	if (scopekind > 0) {
> +		uref = scopekind == 2;
> +		name = (char *)ident;
> +		*(char *)(ident - scopekind) = '\0';
>  		dnkind = DT_NODE_VAR;
>  
>  	} else if (idkind == DT_IDENT_AGG) {
> @@ -2810,7 +2860,7 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
>  	 * errno appropriately and that error will be reported instead.
>  	 */
>  	dt_set_errno(dtp, EDT_NOVAR);
> -	mark = uref ? "``" : "`";
> +	markstr = uref ? "``" : "`";
>  
>  	if (scope == DTRACE_OBJ_EXEC && (
>  	    (dhp != dtp->dt_globals &&
> @@ -2902,13 +2952,13 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
>  				xyerror(D_SYM_MODEL, "cannot use %s symbol "
>  					"%s%s%s in a %s D program\n",
>  					dt_module_modelname(mp), dts.object,
> -					mark, dts.name,
> +					markstr, dts.name,
>  					dt_module_modelname(dtp->dt_ddefs));
>  			}
>  
>  			xyerror(D_SYM_NOTYPES, "no symbolic type information "
>  				"is available for %s%s%s: %s\n",
> -				dts.object, mark, dts.name,
> +				dts.object, markstr, dts.name,
>  				dtrace_errmsg(dtp, dtrace_errno(dtp)));
>  		}
>  
> @@ -3014,7 +3064,7 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
>  
>  	} else if (scope != DTRACE_OBJ_EXEC) {
>  		xyerror(D_IDENT_UNDEF, "failed to resolve %s%s%s: %s\n",
> -		    dnp->dn_string, mark, name,
> +		    dnp->dn_string, markstr, name,
>  		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
>  	} else {
>  		xyerror(D_IDENT_UNDEF, "failed to resolve %s: %s\n",
> diff --git a/libdtrace/dt_parser.h b/libdtrace/dt_parser.h
> index d74d658a..b9149220 100644
> --- a/libdtrace/dt_parser.h
> +++ b/libdtrace/dt_parser.h
> @@ -211,6 +211,7 @@ extern dt_node_t *dt_node_tstring(dt_node_t *, uintmax_t);
>  extern dt_node_t *dt_node_link(dt_node_t *, dt_node_t *);
>  extern dt_node_t *dt_node_cook(dt_node_t *, uint_t);
>  extern void dt_cook_taint_alloca(dt_node_t *, dt_ident_t *, dt_node_t *);
> +extern int dt_scope_parse(const char *, const char **, const char **);
>  
>  extern dt_node_t *dt_node_xalloc(dtrace_hdl_t *, int);
>  extern void dt_node_free(dt_node_t *);
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d
> new file mode 100644
> index 00000000..2a267567
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Dotted kernel module scopes lex as a single external symbol
> + * reference.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(foo.bar`baz);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r
> new file mode 100644
> index 00000000..89a9f9c5
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot-kernel.d: [D_IDENT_UNDEF] line 17: failed to resolve foo.bar`baz: Module is no longer loaded
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d
> new file mode 100644
> index 00000000..6b3dcfda
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Dotted userspace module scopes lex as a single external symbol
> + * reference.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(foo.bar``baz);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r
> new file mode 100644
> index 00000000..7734e29f
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_IDENT_UNDEF.scope-dot.d: [D_IDENT_UNDEF] line 17: failed to resolve foo.bar``baz: Module is no longer loaded
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d
> new file mode 100644
> index 00000000..d332191b
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d
> @@ -0,0 +1,18 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Kernel symbols cannot be referenced using userspace scoping.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(vmlinux``major_names);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r
> new file mode 100644
> index 00000000..718b2b31
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYM_BADREF.scope-kernel-as-user.d: [D_SYM_BADREF] line 16: kernel module 'vmlinux' symbol 'major_names' may not be referenced as a user symbol
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d
> new file mode 100644
> index 00000000..bc7e8b9f
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d
> @@ -0,0 +1,20 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/* @@trigger: testprobe */
> +
> +/*
> + * ASSERTION: Userspace symbols cannot be referenced using kernel scoping.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(&testprobe`main);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r
> new file mode 100644
> index 00000000..87169597
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYM_BADREF.scope-user-as-kernel.d: [D_SYM_BADREF] line 18: user module 'testprobe' symbol 'main' may not be referenced as a kernel symbol
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d
> new file mode 100644
> index 00000000..d7cff89f
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A bare userspace D scoping operator with an empty identifier is a
> + * syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(``);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r
> new file mode 100644
> index 00000000..8e9cd1ff
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-bare-user.d: [D_SYNTAX] line 17: syntax error near "``"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d
> new file mode 100644
> index 00000000..44b4f60e
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A bare D scoping operator with an empty identifier is a syntax
> + * error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(`);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r
> new file mode 100644
> index 00000000..bbba5fe8
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-bare.d: [D_SYNTAX] line 17: syntax error near "`"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d
> new file mode 100644
> index 00000000..5ead7b3e
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A dotted userspace D scoping operator with an empty identifier is
> + * a syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(foo.bar``);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r
> new file mode 100644
> index 00000000..b07c07b1
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-dot-user.d: [D_SYNTAX] line 17: syntax error near "bar``"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d
> new file mode 100644
> index 00000000..d3a927d7
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A userspace D scoping operator with an empty identifier is a
> + * syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(bar``);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r
> new file mode 100644
> index 00000000..dbf319ea
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty-user.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty-user.d: [D_SYNTAX] line 17: syntax error near "bar``"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty.d b/test/unittest/lexer/err.D_SYNTAX.scope-empty.d
> new file mode 100644
> index 00000000..e32b3812
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty.d
> @@ -0,0 +1,18 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: A D scoping operator with an empty identifier is a syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(foo`);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-empty.r b/test/unittest/lexer/err.D_SYNTAX.scope-empty.r
> new file mode 100644
> index 00000000..d78728b2
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-empty.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-empty.d: [D_SYNTAX] line 16: syntax error near "foo`"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-multiple.d b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.d
> new file mode 100644
> index 00000000..5e236c45
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: Multiple D scoping operators in one identifier are a syntax
> + * error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(foo``bar``baz);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-multiple.r b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.r
> new file mode 100644
> index 00000000..cefc56ad
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-multiple.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-multiple.d: [D_SYNTAX] line 17: syntax error near "foo``bar``baz"
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-triple.d b/test/unittest/lexer/err.D_SYNTAX.scope-triple.d
> new file mode 100644
> index 00000000..41f7391c
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-triple.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: More than two consecutive D scoping operator characters are a
> + * syntax error.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(foo```bar);
> +	exit(0);
> +}
> diff --git a/test/unittest/lexer/err.D_SYNTAX.scope-triple.r b/test/unittest/lexer/err.D_SYNTAX.scope-triple.r
> new file mode 100644
> index 00000000..4474c502
> --- /dev/null
> +++ b/test/unittest/lexer/err.D_SYNTAX.scope-triple.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/lexer/err.D_SYNTAX.scope-triple.d: [D_SYNTAX] line 17: syntax error near "foo```bar"
> diff --git a/test/unittest/lexer/tst.scope-user-explicit.d b/test/unittest/lexer/tst.scope-user-explicit.d
> new file mode 100644
> index 00000000..2d649f6f
> --- /dev/null
> +++ b/test/unittest/lexer/tst.scope-user-explicit.d
> @@ -0,0 +1,22 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/* @@trigger: testprobe */
> +/* @@runtest-opts: -e */
> +
> +/*
> + * ASSERTION: Explicit userspace module scoping resolves as a userspace
> + * external symbol reference.
> + */
> +
> +#pragma D option quiet
> +
> +BEGIN
> +{
> +	trace(&testprobe``main);
> +	exit(0);
> +}
> diff --git a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d
> index 56c93d43..4de9322d 100644
> --- a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d
> +++ b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d
> @@ -16,6 +16,6 @@
>  
>  BEGIN
>  {
> -	printf("sizeof(`): %d\n", sizeof(`));
> +	printf("sizeof(`unknown_symbol): %d\n", sizeof(`unknown_symbol));
>  	exit(0);
>  }
> diff --git a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r
> index 462469b7..0f46ad1e 100644
> --- a/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r
> +++ b/test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.r
> @@ -1,2 +1,2 @@
>  -- @@stderr --
> -dtrace: failed to compile script test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d: [D_IDENT_UNDEF] line 19: failed to resolve `: Unknown symbol name
> +dtrace: failed to compile script test/unittest/sizeof/err.D_IDENT_UNDEF.UnknownSymbol.d: [D_IDENT_UNDEF] line 19: failed to resolve `unknown_symbol: Unknown symbol name
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d
> new file mode 100644
> index 00000000..51068c20
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d
> @@ -0,0 +1,16 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: D scoping operators are not permitted in enumerator names.
> + */
> +
> +#pragma D option quiet
> +
> +enum scopeop_enum {
> +	foo`bar
> +};
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r
> new file mode 100644
> index 00000000..06ef912b
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/types/err.D_DECL_SCOPE.scopeop-enumerator.d: [D_DECL_SCOPE] line 15: D scoping operator may not be used in an enumerator name (foo`bar)
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d
> new file mode 100644
> index 00000000..6042e208
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d
> @@ -0,0 +1,19 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: D scoping operators are not permitted in declaration names.
> + */
> +
> +#pragma D option quiet
> +
> +int foo`bar;
> +
> +BEGIN
> +{
> +	exit(1);
> +}
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r
> new file mode 100644
> index 00000000..7b2e6797
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/types/err.D_DECL_SCOPE.scopeop-ident.d: [D_DECL_SCOPE] line 14: D scoping operator may not be used in a declaration name (foo`bar)
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d
> new file mode 100644
> index 00000000..5fe36a00
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d
> @@ -0,0 +1,16 @@
> +/*
> + * Oracle Linux DTrace.
> + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
> + * Licensed under the Universal Permissive License v 1.0 as shown at
> + * http://oss.oracle.com/licenses/upl.
> + */
> +
> +/*
> + * ASSERTION: D scoping operators are not permitted in struct member names.
> + */
> +
> +#pragma D option quiet
> +
> +struct scopeop_struct {
> +	int foo`bar;
> +};
> diff --git a/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r
> new file mode 100644
> index 00000000..978ef79d
> --- /dev/null
> +++ b/test/unittest/types/err.D_DECL_SCOPE.scopeop-member.r
> @@ -0,0 +1,2 @@
> +-- @@stderr --
> +dtrace: failed to compile script test/unittest/types/err.D_DECL_SCOPE.scopeop-member.d: [D_DECL_SCOPE] line 15: D scoping operator may not be used in a member name (foo`bar)
> -- 
> 2.52.0
> 
> 
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
> 




More information about the DTrace-devel mailing list