[PATCH] fortify: Refuse to use strcat() on dynamically sized strings

From: Kees Cook

Date: Fri Apr 05 2024 - 14:08:41 EST


Limit the use of strcat() to things we can determine at compile time to
be safe.

Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
---
include/linux/fortify-string.h | 6 ++++++
lib/string_helpers.c | 2 ++
2 files changed, 8 insertions(+)

diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
index 171982e53c9a..8797c06b46e1 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -57,6 +57,7 @@ void __read_overflow2(void) __compiletime_error("detected read beyond size of ob
void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?");
void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)");
void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("detected write beyond size of field (1st parameter); maybe use struct_group()?");
+void __fortify_refuse_strcat(void) __compiletime_warning("Do not use strcat() on dynamically sized destination or source strings");

#define __compiletime_strlen(p) \
({ \
@@ -411,8 +412,13 @@ __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2)
char *strcat(char * const POS p, const char *q)
{
const size_t p_size = __member_size(p);
+ const size_t q_size = __member_size(q);
const size_t wanted = strlcat(p, q, p_size);

+ if (!__builtin_constant_p(p_size) || !__builtin_constant_p(q_size) ||
+ p_size == SIZE_MAX || q_size == SIZE_MAX)
+ __fortify_refuse_strcat();
+
if (p_size <= wanted)
fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p_size, wanted + 1, p);
return p;
diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index 169eaf583494..27b9d97c60c5 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -1019,6 +1019,8 @@ void __read_overflow2_field(size_t avail, size_t wanted) { }
EXPORT_SYMBOL(__read_overflow2_field);
void __write_overflow_field(size_t avail, size_t wanted) { }
EXPORT_SYMBOL(__write_overflow_field);
+void __fortify_refuse_strcat(void) { }
+EXPORT_SYMBOL(__fortify_refuse_strcat);

static const char * const fortify_func_name[] = {
#define MAKE_FORTIFY_FUNC_NAME(func) [MAKE_FORTIFY_FUNC(func)] = #func
--
2.34.1


--
Kees Cook