[PATCH v11 32/65] dyndbg: Harden classmap and callsite validation

Jim Cromie posted 65 patches 3 weeks, 3 days ago
[PATCH v11 32/65] dyndbg: Harden classmap and callsite validation
Posted by Jim Cromie 3 weeks, 3 days ago
Dynamic debug classmaps allow modules to _DEFINE or _USE classmaps,
which map classnames to a range of class-IDs (0..62) that they are
using.  This names => IDs mapping allows many modules to use 0..N
independently, and even allows (carefully) shared classnames.

Previously, class validation only caught some violations in _DEFINEd
classmaps; 0..62 range checks, overlaps among _DEFINEd classmaps, but
left blind spots in _USEs, for out of bounds manual offsets,
overlapping _USEd classmaps, unknown map types, and orphaned callsite
IDs. These oversights could lead to ambiguous routing or spammy
warnings during control query execution.

This commit hardens classmap and descriptor validation:

- Mirror the compile-time limits of _DEFINE by adding a static_assert
  to validate the _offset value passed to DYNAMIC_DEBUG_CLASSMAP_USE_.
- Add run-time overlap checks for _USEd classmaps in ddebug_add_module()
  to prevent collisions between private maps and imported APIs.
- Validate that module classmaps use known enum map types.
- Scan module descriptors at load time to print a single warning per
  missing class_id, rather than waiting for a user query to trip over it.
- Downgrade the global WARN_ONCE in ddebug_match_desc() to a
  pr_warn_ratelimited, since orphaned class IDs are now tracked and
  warned about early at module load.

Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
 include/linux/dynamic_debug.h |  2 ++
 lib/dynamic_debug.c           | 52 +++++++++++++++++++++++++++++++----
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 0d1245aefc69..28684cfc0937 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -262,6 +262,8 @@ struct _ddebug_class_param {
 
 #define __DYNAMIC_DEBUG_CLASSMAP_USE(_var, _offset, _uname)		\
 	extern struct _ddebug_class_map _var;				\
+	static_assert((_offset) >= 0 && (_offset) < _DPRINTK_CLASS_DFLT, \
+		      "classmap use offset must be in 0..62");		\
 	static struct _ddebug_class_user __aligned(8) __used		\
 	__section("__dyndbg_class_users") _uname = {			\
 		.mod_name = KBUILD_MODNAME,				\
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 2083a8546460..635df6edb4cf 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -320,7 +320,7 @@ static bool ddebug_match_desc(const struct ddebug_query *query,
 	/* site is class'd */
 	site_map = ddebug_find_map_by_class_id(di, dp->class_id);
 	if (!site_map) {
-		WARN_ONCE(1, "unknown class_id %d, check %s's CLASSMAP definitions",
+		pr_warn_ratelimited("unknown class_id %d, check %s's CLASSMAP definitions\n",
 			  dp->class_id, di->mod_name);
 		return false;
 	}
@@ -1404,9 +1404,8 @@ static void ddebug_apply_class_users(const struct _ddebug_info *di)
 	(__dst)->info._vec.len = __nc;					\
 })
 
-static int __maybe_unused
-ddebug_class_range_overlap(struct _ddebug_class_map *cm,
-			   u64 *reserved_ids)
+static int ddebug_class_range_overlap(struct _ddebug_class_map *cm,
+				      u64 *reserved_ids)
 {
 	u64 range = (((1ULL << cm->length) - 1) << cm->base);
 
@@ -1420,6 +1419,23 @@ ddebug_class_range_overlap(struct _ddebug_class_map *cm,
 	return 0;
 }
 
+static int ddebug_class_user_overlap(struct _ddebug_class_user *cli,
+				     u64 *reserved_ids)
+{
+	struct _ddebug_class_map *cm = cli->map;
+	int base = cm->base + cli->offset;
+	u64 range = (((1ULL << cm->length) - 1) << base);
+
+	if (range & *reserved_ids) {
+		pr_err("[%d..%d] (from %s) conflicts with %llx\n", base,
+		       base + cm->length - 1, cm->class_names[0],
+		       *reserved_ids);
+		return -EINVAL;
+	}
+	*reserved_ids |= range;
+	return 0;
+}
+
 /*
  * Allocate a new ddebug_table for the given module
  * and add it to the global list.
@@ -1430,6 +1446,7 @@ static int ddebug_add_module(struct _ddebug_info *di)
 	struct _ddebug_class_map *cm;
 	struct _ddebug_class_user *cli;
 	u64 reserved_ids = 0;
+	u64 bad_ids = 0;
 	int i;
 
 	if (!di->descs.len)
@@ -1454,10 +1471,33 @@ static int ddebug_add_module(struct _ddebug_info *di)
 	dd_mark_vector_subrange(i, cli, &dt->info, users, dt);
 	/* now di is stale */
 
-	/* insure 2+ classmaps share the per-module 0..62 class_id space */
-	for_subvec(i, cm, &dt->info, maps)
+	/* validate class map types and the per-module 0..62 class_id space */
+	for_subvec(i, cm, &dt->info, maps) {
+		if (cm->map_type > DD_CLASS_TYPE_LEVEL_NUM) {
+			pr_err("module %s has unknown classmap type %d\n", dt->info.mod_name, cm->map_type);
+			goto cleanup;
+		}
 		if (ddebug_class_range_overlap(cm, &reserved_ids))
 			goto cleanup;
+	}
+
+	for_subvec(i, cli, &dt->info, users)
+		if (ddebug_class_user_overlap(cli, &reserved_ids))
+			goto cleanup;
+
+	/* validate all class_ids against module's classmaps/users */
+	for (i = 0; i < dt->info.descs.len; i++) {
+		struct _ddebug *dp = &dt->info.descs.start[i];
+
+		if (dp->class_id == _DPRINTK_CLASS_DFLT)
+			continue;
+		if (bad_ids & (1ULL << dp->class_id))
+			continue;
+		if (!ddebug_find_map_by_class_id(&dt->info, dp->class_id)) {
+			pr_warn("module %s uses unknown class_id %d\n", dt->info.mod_name, dp->class_id);
+			bad_ids |= (1ULL << dp->class_id);
+		}
+	}
 
 	mutex_lock(&ddebug_lock);
 	list_add_tail(&dt->link, &ddebug_tables);
-- 
2.53.0
Re: [PATCH v11 32/65] dyndbg: Harden classmap and callsite validation
Posted by Louis Chauvet 2 weeks, 3 days ago
On Fri, 13 Mar 2026 07:19:57 -0600, Jim Cromie <jim.cromie@gmail.com> wrote:
> diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
> index 0d1245aefc69..28684cfc0937 100644
> --- a/include/linux/dynamic_debug.h
> +++ b/include/linux/dynamic_debug.h
> @@ -262,6 +262,8 @@ struct _ddebug_class_param {
>  
>  #define __DYNAMIC_DEBUG_CLASSMAP_USE(_var, _offset, _uname)		\
>  	extern struct _ddebug_class_map _var;				\
> +	static_assert((_offset) >= 0 && (_offset) < _DPRINTK_CLASS_DFLT, \
> +		      "classmap use offset must be in 0..62");		\

Can't you also check the offset+base?

>
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index 2083a8546460..635df6edb4cf 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
> @@ -1404,9 +1404,8 @@ static void ddebug_apply_class_users(const struct _ddebug_info *di)
>  	(__dst)->info._vec.len = __nc;					\
>  })
>  
> -static int __maybe_unused
> -ddebug_class_range_overlap(struct _ddebug_class_map *cm,
> -			   u64 *reserved_ids)
> +static int ddebug_class_range_overlap(struct _ddebug_class_map *cm,
> +				      u64 *reserved_ids)

I think you can remove the __maybe_unused earlier in the series (23/65
seems to use it).

> @@ -1454,10 +1471,33 @@ static int ddebug_add_module(struct _ddebug_info *di)
> [ ... skip 5 lines ... ]
> +	/* validate class map types and the per-module 0..62 class_id space */
> +	for_subvec(i, cm, &dt->info, maps) {
> +		if (cm->map_type > DD_CLASS_TYPE_LEVEL_NUM) {
> +			pr_err("module %s has unknown classmap type %d\n", dt->info.mod_name, cm->map_type);
> +			goto cleanup;
> +		}

I think this check could be inserted earlier.

-- 
Louis Chauvet <louis.chauvet@bootlin.com>
Re: [PATCH v11 32/65] dyndbg: Harden classmap and callsite validation
Posted by jim.cromie@gmail.com 2 weeks, 3 days ago
On Fri, Mar 20, 2026 at 10:42 AM Louis Chauvet
<louis.chauvet@bootlin.com> wrote:
>
> On Fri, 13 Mar 2026 07:19:57 -0600, Jim Cromie <jim.cromie@gmail.com> wrote:
> > diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
> > index 0d1245aefc69..28684cfc0937 100644
> > --- a/include/linux/dynamic_debug.h
> > +++ b/include/linux/dynamic_debug.h
> > @@ -262,6 +262,8 @@ struct _ddebug_class_param {
> >
> >  #define __DYNAMIC_DEBUG_CLASSMAP_USE(_var, _offset, _uname)          \
> >       extern struct _ddebug_class_map _var;                           \
> > +     static_assert((_offset) >= 0 && (_offset) < _DPRINTK_CLASS_DFLT, \
> > +                   "classmap use offset must be in 0..62");          \
>
> Can't you also check the offset+base?

If I dont already check, I can. theyre all constants.

>
> >
> > diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> > index 2083a8546460..635df6edb4cf 100644
> > --- a/lib/dynamic_debug.c
> > +++ b/lib/dynamic_debug.c
> > @@ -1404,9 +1404,8 @@ static void ddebug_apply_class_users(const struct _ddebug_info *di)
> >       (__dst)->info._vec.len = __nc;                                  \
> >  })
> >
> > -static int __maybe_unused
> > -ddebug_class_range_overlap(struct _ddebug_class_map *cm,
> > -                        u64 *reserved_ids)
> > +static int ddebug_class_range_overlap(struct _ddebug_class_map *cm,
> > +                                   u64 *reserved_ids)
>
> I think you can remove the __maybe_unused earlier in the series (23/65
> seems to use it).

yeah that sounds correct.

>
> > @@ -1454,10 +1471,33 @@ static int ddebug_add_module(struct _ddebug_info *di)
> > [ ... skip 5 lines ... ]
> > +     /* validate class map types and the per-module 0..62 class_id space */
> > +     for_subvec(i, cm, &dt->info, maps) {
> > +             if (cm->map_type > DD_CLASS_TYPE_LEVEL_NUM) {
> > +                     pr_err("module %s has unknown classmap type %d\n", dt->info.mod_name, cm->map_type);
> > +                     goto cleanup;
> > +             }
>
> I think this check could be inserted earlier.

ya this could be compile-time too.

>
> --
> Louis Chauvet <louis.chauvet@bootlin.com>