Skip to contentSkip to conversation

usdt_parser: harden DOF and note bounds checks

PATCH New Kris Van Hees 1 participant 1 message Jun 12

lore
Share & tools
lore
#0 lore raw

PATCH 2/6 usdt_parser: harden DOF and note bounds checks

Validate DOF section metadata before use, require ELF note strings to terminate within their declared payloads, and check parsed DOF record sizes before DTrace consumes them. This keeps malformed parser input or stash data from driving out-of-bounds reads in either the parser or the host.

Orabug: 39352038
Orabug: 39352051
Orabug: 39351967
CVE: CVE-2026-46831

---
 include/dtrace/pid.h          |   2 +-
 libcommon/usdt_parser.h       |  10 ++-
 libcommon/usdt_parser_dof.c   |  25 +++---
 libcommon/usdt_parser_notes.c | 106 +++++++++++++++++++------
 libdtrace/dt_pid.c            | 142 ++++++++++++++++++++++++++++++----
 5 files changed, 233 insertions(+), 52 deletions(-)
include/dtrace/pid.h +1 -1
index 4a239b07..88f9366c 100644
@@ -45,7 +45,7 @@ typedef struct pid_probespec {
 	size_t pps_nargvlen;			/* (high estimate of) length of array */
 	char *pps_xargv;			/* array of xlated args */
 	size_t pps_xargvlen;			/* (high estimate of) length of array */
-	int8_t *pps_argmap;			/* mapped arg indexes */
+	uint8_t *pps_argmap;			/* mapped arg indexes */
 	char *pps_sargv;			/* list of arg sources */
 	int pps_flags;				/* flags */
 
libcommon/usdt_parser.h +9 -1
index b11207d1..99fc3033 100644
@@ -136,7 +136,7 @@ typedef struct dof_parsed {
 			 * Mapping from native arg index to xlated arg index.
 			 * xargc in length.
 			 */
-			int8_t argmap[1];
+			uint8_t argmap[1];
 		} argmap;
 
 		struct dpi_tracepoint_info {
@@ -171,6 +171,14 @@ typedef struct dof_parsed {
 	};
 } dof_parsed_t;
 
+#define DIT_PROVIDER_HEADSZ	offsetof(dof_parsed_t, provider.name)
+#define DIT_PROBE_HEADSZ	offsetof(dof_parsed_t, probe.name)
+#define DIT_TRACEPOINT_HEADSZ	offsetof(dof_parsed_t, tracepoint.args)
+#define DIT_ERR_HEADSZ		offsetof(dof_parsed_t, err.err)
+#define DIT_ARGS_NATIVE_HEADSZ	offsetof(dof_parsed_t, nargs.args)
+#define DIT_ARGS_XLAT_HEADSZ	offsetof(dof_parsed_t, xargs.args)
+#define DIT_ARGS_MAP_HEADSZ	offsetof(dof_parsed_t, argmap.argmap)
+
 /*
  * Host-side: in usdt_parser_host.c.
  * The host is the non-jailed process that talks to the jailed parser.
libcommon/usdt_parser_dof.c +11 -14
index bc8e185a..e2166985 100644
@@ -275,8 +275,8 @@ dof_slurp(int out, dof_hdr_t *dof, uint64_t ubase)
 		return -1;
 	}
 
-	if (dof->dofh_secsize == 0) {
-		usdt_error(out, EINVAL, "zero section header size");
+	if (dof->dofh_secsize != sizeof(dof_sec_t)) {
+		usdt_error(out, EINVAL, "incorrect section header size");
 		return -1;
 	}
 
@@ -462,7 +462,7 @@ validate_provider(int out, dof_hdr_t *dof, dof_sec_t *sec)
 		return -1;
 	}
 
-	if (prb_sec->dofs_entsize == 0 ||
+	if (prb_sec->dofs_entsize < sizeof(dof_probe_t) ||
 	    prb_sec->dofs_entsize > prb_sec->dofs_size) {
 		usdt_error(out, EINVAL, "invalid entry size %x, max %lx",
 			   prb_sec->dofs_entsize, prb_sec->dofs_size);
@@ -615,11 +615,11 @@ validate_provider(int out, dof_hdr_t *dof, dof_sec_t *sec)
 		typeidx = prb->dofpr_xargv;
 		typestr = strtab + prb->dofpr_xargv;
 		for (k = 0; k < prb->dofpr_xargc; k++) {
-			if (arg[prb->dofpr_argidx + k] > prb->dofpr_nargc) {
+			if (arg[prb->dofpr_argidx + k] >= prb->dofpr_nargc) {
 				usdt_error(out, EINVAL, "bad native argument index "
 					   "for arg %i: %i (max %i)", k,
 					   arg[prb->dofpr_argidx + k],
-					   prb->dofpr_nargc);
+					   prb->dofpr_nargc - 1);
 				return -1;
 			}
 
@@ -748,7 +748,7 @@ emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
 	 * flags.
 	 */
 
-	msg_size = offsetof(dof_parsed_t, probe.name) +
+	msg_size = DIT_PROBE_HEADSZ +
 		   strlen(dhpb->dthpb_mod) + 1 +
 		   strlen(dhpb->dthpb_func) + 1 +
 		   strlen(dhpb->dthpb_name) + 1;
@@ -786,7 +786,7 @@ emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
 		size_t	nargs_size;
 
 		nargs_size = strings_len(dhpb->dthpb_ntypes, dhpb->dthpb_nargc);
-		msg_size = offsetof(dof_parsed_t, nargs.args) + nargs_size;
+		msg_size = DIT_ARGS_NATIVE_HEADSZ + nargs_size;
 
 		msg = malloc(msg_size);
 		if (!msg)
@@ -808,8 +808,7 @@ emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
 
 			xargs_size = strings_len(dhpb->dthpb_xtypes,
 						 dhpb->dthpb_xargc);
-			msg_size = offsetof(dof_parsed_t, xargs.args) +
-				   xargs_size;
+			msg_size = DIT_ARGS_XLAT_HEADSZ + xargs_size;
 
 			msg = malloc(msg_size);
 			if (!msg)
@@ -826,9 +825,8 @@ emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
 
 			/* Then the mapping table. */
 
-			map_size = dhpb->dthpb_xargc * sizeof(int8_t);
-			msg_size = offsetof(dof_parsed_t, argmap.argmap) +
-				   map_size;
+			map_size = dhpb->dthpb_xargc * sizeof(uint8_t);
+			msg_size = DIT_ARGS_MAP_HEADSZ + map_size;
 
 			msg = malloc(msg_size);
 			if (!msg)
@@ -922,8 +920,7 @@ emit_provider(int out, dof_helper_t *dhp,
 	}
 
 	dhpb.dthpb_prov = strtab + prov->dofpv_name;
-	provider_msg_size = offsetof(dof_parsed_t, provider.name) +
-	    strlen(dhpb.dthpb_prov) + 1;
+	provider_msg_size = DIT_PROVIDER_HEADSZ + strlen(dhpb.dthpb_prov) + 1;
 
 	provider_msg = malloc(provider_msg_size);
 	if (!provider_msg) {
libcommon/usdt_parser_notes.c +84 -22

218 lines not on this page · raw message

libdtrace/dt_pid.c +128 -14

247 lines not on this page · raw message

Signature · 1 line
2.47.3