Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH v1 1/2] landlock: Bound escaped trace path output
@ 2026-09-07 15:43 Mickaël Salaün
  2026-09-07 15:43 ` [PATCH v1 2/2] landlock: Test trace path output boundaries Mickaël Salaün
  0 siblings, 1 reply; 2+ messages in thread
From: Mickaël Salaün @ 2026-09-07 15:43 UTC (permalink / raw)
  To: Günther Noack; +Cc: Mickaël Salaün, linux-security-module

Filesystem paths may expand fourfold when trace text escapes spaces and
other untrusted bytes.  A sufficiently long representation can exhaust
the shared scratch sequence.  A sibling __print_flags() helper may then
return an unterminated one-past pointer because TP_printk() argument
ordering is unspecified.

Use a fixed budget rather than the scratch space available at call time,
so output does not vary with sibling evaluation order.  Limit an
untrusted string to three quarters of the trace sequence, leaving the
rest for sibling helpers and final event metadata.  Compute and commit
complete escaped output transactionally so an exact fill cannot consume
the terminating NUL or poison the scratch sequence.

For strings that exceed the limit, retain the largest prefix ending at a
complete escape unit, then append a raw UTF-8 ellipsis.  Keep the
helper's existing octal fallback so complete values remain unchanged.
Hex fallback would consume the same four bytes per escaped byte without
increasing the prefix or strengthening the marker.  ESCAPE_NAP renders
every non-ASCII input byte in octal, so legitimate data cannot reproduce
the marker without being escaped.

Cc: Günther Noack <gnoack@google.com>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
---
 include/trace/events/landlock.h | 70 +++++++++++++++++++++++++--------
 1 file changed, 53 insertions(+), 17 deletions(-)

diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h
index f82588f6f90e..d05253afaf59 100644
--- a/include/trace/events/landlock.h
+++ b/include/trace/events/landlock.h
@@ -28,6 +28,16 @@ struct task_struct;
 
 #ifdef CREATE_TRACE_POINTS
 
+/* About 6 KiB, leaving about 2 KiB for sibling helpers and fixed fields. */
+#define TRACE_UNTRUSTED_STR_OUTPUT_SIZE \
+	(TRACE_SEQ_BUFFER_SIZE - TRACE_SEQ_BUFFER_SIZE / 4)
+
+/*
+ * A raw UTF-8 ellipsis (…) marks truncation and cannot collide with escaped
+ * input: ESCAPE_NAP renders every non-ASCII input byte in octal.
+ */
+#define TRACE_TRUNCATION_MARKER "\xe2\x80\xa6"
+
 /*
  * Escapes @len bytes of an untrusted string into the trace sequence @p so it
  * cannot inject field separators or control characters into the ftrace text
@@ -37,33 +47,59 @@ struct task_struct;
  * NUL-terminated or carries embedded NUL bytes (an abstract socket name) is
  * escaped in full instead of being truncated at the first NUL.
  *
- * Return: a pointer into @p's buffer, or NULL if @src is NULL or the buffer is
- * exhausted (normal when the trace buffer is full).
+ * Strings that exceed the output limit retain the largest complete escaped
+ * prefix followed by the truncation marker.
+ *
+ * Return: a pointer into @p's buffer, or NULL if @src is NULL or the fixed
+ * output reservation is unavailable.
  */
 static inline const char *
 __trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len)
 {
+	const unsigned int escape_flags = ESCAPE_SPACE | ESCAPE_SPECIAL |
+					  ESCAPE_NAP | ESCAPE_APPEND |
+					  ESCAPE_OCTAL;
+	const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1;
+	size_t buf_size, prefix_len, prefix_size;
 	int escaped_size;
 	char *buf;
-	size_t buf_size = seq_buf_get_buf(&p->seq, &buf);
-	const char *ret = trace_seq_buffer_ptr(p);
+	const char *ret;
 
-	/* Buffer exhaustion is normal when the trace buffer is full. */
-	if (!src || buf_size == 0)
+	buf_size = seq_buf_get_buf(&p->seq, &buf);
+	if (!src || buf_size < TRACE_UNTRUSTED_STR_OUTPUT_SIZE)
 		return NULL;
 
-	escaped_size =
-		string_escape_mem(src, len, buf, buf_size,
-				  ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NAP |
-					  ESCAPE_APPEND | ESCAPE_OCTAL,
-				  " ='\"\\");
-	if (unlikely(escaped_size >= buf_size)) {
-		/* We need some room for the final '\0'. */
-		seq_buf_set_overflow(&p->seq);
-		p->full = 1;
-		return NULL;
+	ret = trace_seq_buffer_ptr(p);
+	escaped_size = string_escape_mem(src, len, buf,
+					 TRACE_UNTRUSTED_STR_OUTPUT_SIZE,
+					 escape_flags, " ='\"\\");
+	if (likely(escaped_size < TRACE_UNTRUSTED_STR_OUTPUT_SIZE)) {
+		seq_buf_commit(&p->seq, escaped_size);
+		trace_seq_putc(p, 0);
+		return ret;
+	}
+
+	prefix_len = 0;
+	prefix_size = 0;
+	while (prefix_len < len) {
+		const char *const src_char = src + prefix_len;
+		int char_size;
+
+		char_size = string_escape_mem(src_char, 1, NULL, 0,
+					      escape_flags, " ='\"\\");
+		if (char_size > TRACE_UNTRUSTED_STR_OUTPUT_SIZE - marker_len -
+					1 - prefix_size)
+			break;
+		prefix_size += char_size;
+		prefix_len++;
 	}
-	seq_buf_commit(&p->seq, escaped_size);
+
+	escaped_size = string_escape_mem(src, prefix_len, buf, prefix_size,
+					 escape_flags, " ='\"\\");
+	if (WARN_ON_ONCE(escaped_size != prefix_size))
+		return NULL;
+	memcpy(buf + prefix_size, TRACE_TRUNCATION_MARKER, marker_len);
+	seq_buf_commit(&p->seq, prefix_size + marker_len);
 	trace_seq_putc(p, 0);
 	return ret;
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-07 15:44 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07 15:43 [PATCH v1 1/2] landlock: Bound escaped trace path output Mickaël Salaün
2026-09-07 15:43 ` [PATCH v1 2/2] landlock: Test trace path output boundaries Mickaël Salaün

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox