Add BUFFER_FIELD macro for safe pretty-printing of flexible array members (#40273)
Message structs with flexible array members (char Buffer[], char Content[]) used FIELD() in PRETTY_PRINT, which streams the member as a C-string until a NUL byte. Add a BUFFER_FIELD macro and PrettyPrintSafeBufferView helper that bounds the read using Header.MessageSize. Also add std::string_view handling to PrettyPrint. Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ben Hillis committed
Apr 22, 2026 at 14:46 UTC
8e1fafb99fb26afd3709a89a0e18af2b0abff245
2 files changed
+33
-11
src/shared/inc/lxinitshared.h
+10
-10
@@ -579,7 +579,7 @@ typedef struct _LX_PROCESS_CRASH
579
580
char Buffer[];
581
582
- PRETTY_PRINT(FIELD(Header), FIELD(Timestamp), FIELD(Signal), FIELD(Pid), FIELD(Buffer));
582
+ PRETTY_PRINT(FIELD(Header), FIELD(Timestamp), FIELD(Signal), FIELD(Pid), BUFFER_FIELD(Buffer));
583
584
} LX_PROCESS_CRASH, *PLX_PROCESS_CRASH;
585
@@ -701,7 +701,7 @@ typedef struct _LX_INIT_CREATE_LOGIN_SESSION
701
unsigned int Gid;
702
char Buffer[]; // Contains username
703
704
- PRETTY_PRINT(FIELD(Header), FIELD(Uid), FIELD(Gid), FIELD(Buffer));
704
+ PRETTY_PRINT(FIELD(Header), FIELD(Uid), FIELD(Gid), BUFFER_FIELD(Buffer));
705
} LX_INIT_CREATE_LOGIN_SESSION, *PLX_INIT_CREATE_LOGIN_SESSION;
706
707
//
@@ -716,7 +716,7 @@ typedef struct _LX_INIT_QUERY_ENVIRONMENT_VARIABLE
716
MESSAGE_HEADER Header;
717
char Buffer[];
718
719
- PRETTY_PRINT(FIELD(Header), FIELD(Buffer));
719
+ PRETTY_PRINT(FIELD(Header), BUFFER_FIELD(Buffer));
720
} LX_INIT_QUERY_ENVIRONMENT_VARIABLE, *PLX_INIT_QUERY_ENVIRONMENT_VARIABLE;
721
722
typedef struct _LX_GNS_RESULT
@@ -739,7 +739,7 @@ typedef struct _LX_GNS_INTERFACE_CONFIGURATION
739
MESSAGE_HEADER Header;
740
char Content[];
741
742
- PRETTY_PRINT(FIELD(Header), FIELD(Content));
742
+ PRETTY_PRINT(FIELD(Header), BUFFER_FIELD(Content));
743
} LX_GNS_INTERFACE_CONFIGURATION, *PLX_GNS_INTERFACE_CONFIGURATION;
744
745
typedef struct _LX_GNS_NOTIFICATION
@@ -751,7 +751,7 @@ typedef struct _LX_GNS_NOTIFICATION
751
GUID AdapterId;
752
char Content[];
753
754
- PRETTY_PRINT(FIELD(Header), FIELD(AdapterId), FIELD(Content));
754
+ PRETTY_PRINT(FIELD(Header), FIELD(AdapterId), BUFFER_FIELD(Content));
755
} LX_GNS_NOTIFICATION, *PLX_GNS_NOTIFICATION;
756
757
typedef struct _LX_GNS_PORT_ALLOCATION_REQUEST
@@ -845,7 +845,7 @@ typedef struct _LX_GNS_JSON_MESSAGE
845
MESSAGE_HEADER Header;
846
char Content[];
847
848
- PRETTY_PRINT(FIELD(Header), FIELD(Content));
848
+ PRETTY_PRINT(FIELD(Header), BUFFER_FIELD(Content));
849
} LX_GNS_JSON_MESSAGE, *PLX_GNS_JSON_MESSAGE;
850
851
using PCLX_INIT_NETWORK_INFORMATION = const LX_INIT_NETWORK_INFORMATION*;
@@ -1288,7 +1288,7 @@ typedef struct _LX_MINI_INIT_TELEMETRY_MESSAGE
1288
bool ShowDrvFsNotification;
1289
char Buffer[];
1290
1291
- PRETTY_PRINT(FIELD(Header), FIELD(ShowDrvFsNotification), FIELD(Buffer));
1291
+ PRETTY_PRINT(FIELD(Header), FIELD(ShowDrvFsNotification), BUFFER_FIELD(Buffer));
1292
1293
} LX_MINI_INIT_TELEMETRY_MESSAGE, *PLX_MINI_INIT_TELEMETRY_MESSAGE;
1294
@@ -1319,7 +1319,7 @@ typedef struct _LX_MINI_INIT_UNMOUNT_MESSAGE
1319
MESSAGE_HEADER Header;
1320
char Buffer[];
1321
1322
- PRETTY_PRINT(FIELD(Header), FIELD(Buffer));
1322
+ PRETTY_PRINT(FIELD(Header), BUFFER_FIELD(Buffer));
1323
} LX_MINI_INIT_UNMOUNT_MESSAGE, *PLX_MINI_INIT_UNMOUNT_MESSAGE;
1324
1325
typedef struct _LX_MINI_INIT_DETACH_MESSAGE
@@ -1368,7 +1368,7 @@ typedef struct _LX_INIT_GUEST_CAPABILITIES
1368
bool SeccompAvailable;
1369
char Buffer[]; // Contains the kernel version string
1370
1371
- PRETTY_PRINT(FIELD(Header), FIELD(SeccompAvailable), FIELD(Buffer));
1371
+ PRETTY_PRINT(FIELD(Header), FIELD(SeccompAvailable), BUFFER_FIELD(Buffer));
1372
} LX_INIT_GUEST_CAPABILITIES, *PLX_INIT_GUEST_CAPABILITIES;
1373
1374
typedef struct _LX_MINI_INIT_WAIT_FOR_PMEM_DEVICE_MESSAGE
@@ -1515,7 +1515,7 @@ typedef struct _LX_INIT_QUERY_VM_ID
1515
MESSAGE_HEADER Header;
1516
char Buffer[];
1517
1518
- PRETTY_PRINT(FIELD(Header), FIELD(Buffer));
1518
+ PRETTY_PRINT(FIELD(Header), BUFFER_FIELD(Buffer));
1519
} LX_INIT_QUERY_VM_ID, *PLX_INIT_QUERY_VM_ID;
1520
1521
template <>
src/shared/inc/prettyprintshared.h
+23
-1
@@ -17,6 +17,8 @@ Abstract:
17
#pragma once
18
19
#include <sstream>
20
+#include <cstring>
21
+#include <string_view>
22
23
#include "defs.h"
24
#include "stringshared.h"
@@ -31,6 +33,22 @@ Abstract:
33
34
#define STRING_FIELD(Name) #Name, (Name <= 0 ? "<empty>" : ((char*)(this)) + Name)
35
36
+// Safe pretty-print for flexible array members (char Buffer[]). Bounds the read
37
+// using the struct's Header.MessageSize so it never reads past the received data.
38
+#define BUFFER_FIELD(Name) #Name, PrettyPrintSafeBufferView(this, Header.MessageSize, Name)
39
+
40
+inline std::string_view PrettyPrintSafeBufferView(const void* structBase, unsigned int messageSize, const char* buffer)
41
+{
42
+ const auto offset = static_cast<size_t>(buffer - reinterpret_cast<const char*>(structBase));
43
+ if (offset >= messageSize)
44
+ {
45
+ return "<out-of-bounds>";
46
+ }
47
+
48
+ const size_t maxLen = messageSize - offset;
49
+ return std::string_view(buffer, strnlen(buffer, maxLen));
50
+}
51
+
52
#define PRETTY_PRINT(...) \
53
void PrettyPrintImpl(std::stringstream& Out) const \
54
{ \
@@ -47,7 +65,11 @@ Abstract:
65
template <typename T>
66
inline void PrettyPrint(std::stringstream& Out, const T& Value)
67
{
50
- if constexpr (std::is_same_v<T, const char*> || std::is_same_v<T, char[]>)
68
+ if constexpr (std::is_same_v<T, std::string_view>)
69
+ {
70
+ Out << Value;
71
+ }
72
+ else if constexpr (std::is_same_v<T, const char*> || std::is_same_v<T, char[]>)
73
{
74
if (Value == nullptr)
75
{